@salesforce/storefront-next-runtime 0.1.1 → 0.2.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.
- package/dist/DesignComponent.js +150 -0
- package/dist/DesignComponent.js.map +1 -0
- package/dist/DesignFrame.js +196 -0
- package/dist/DesignFrame.js.map +1 -0
- package/dist/DesignRegion.js +83 -0
- package/dist/DesignRegion.js.map +1 -0
- package/dist/apply-url-config.js +130 -0
- package/dist/apply-url-config.js.map +1 -0
- package/dist/component.types.d.ts +87 -0
- package/dist/component.types.d.ts.map +1 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.js +0 -0
- package/dist/design-data.d.ts +983 -0
- package/dist/design-data.d.ts.map +1 -0
- package/dist/design-data.js +908 -0
- package/dist/design-data.js.map +1 -0
- package/dist/design-messaging.d.ts +2 -2
- package/dist/design-react-core.d.ts +48 -3
- package/dist/design-react-core.d.ts.map +1 -1
- package/dist/design-react-core.js +81 -2
- package/dist/design-react-core.js.map +1 -1
- package/dist/design-react.d.ts +20 -95
- package/dist/design-react.d.ts.map +1 -1
- package/dist/design-react.js +3 -485
- package/dist/design-styles.css +2 -1
- package/dist/design.d.ts +110 -2
- package/dist/design.d.ts.map +1 -0
- package/dist/events.d.ts +1 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/index.d.ts +1110 -154
- package/dist/index.d.ts.map +1 -1
- package/dist/multi-site.d.ts +154 -0
- package/dist/multi-site.d.ts.map +1 -0
- package/dist/multi-site.js +393 -0
- package/dist/multi-site.js.map +1 -0
- package/dist/routing-app-wrapper.d.ts +18 -0
- package/dist/routing-app-wrapper.d.ts.map +1 -0
- package/dist/routing-app-wrapper.js +21 -0
- package/dist/routing-app-wrapper.js.map +1 -0
- package/dist/routing.d.ts +42 -0
- package/dist/routing.d.ts.map +1 -0
- package/dist/routing.js +175 -0
- package/dist/routing.js.map +1 -0
- package/dist/scapi.d.ts +69 -5
- package/dist/scapi.d.ts.map +1 -1
- package/dist/scapi.js +1 -1
- package/dist/scapi.js.map +1 -1
- package/dist/types.d.ts +40 -13289
- package/dist/types.d.ts.map +1 -1
- package/dist/types2.d.ts +13293 -0
- package/dist/types2.d.ts.map +1 -0
- package/dist/types3.d.ts +110 -0
- package/dist/types3.d.ts.map +1 -0
- package/dist/workspace.d.ts +46 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +52 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +45 -2
- package/dist/design-react.js.map +0 -1
- package/dist/index2.d.ts +0 -1171
- package/dist/index2.d.ts.map +0 -1
- /package/{LICENSE.txt → LICENSE} +0 -0
|
@@ -0,0 +1,983 @@
|
|
|
1
|
+
import { r as ShopperExperience } from "./types2.js";
|
|
2
|
+
|
|
3
|
+
//#region src/design/data/types.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A manifest containing all variations of a single Page Designer page for a
|
|
7
|
+
* specific locale. Variations are evaluated in {@link variationOrder} sequence;
|
|
8
|
+
* the first whose visibility rule passes is selected. If none match, the
|
|
9
|
+
* {@link defaultVariation} is used as a fallback.
|
|
10
|
+
*/
|
|
11
|
+
interface PageManifest {
|
|
12
|
+
/** The unique identifier of the page this manifest represents. */
|
|
13
|
+
pageId: string;
|
|
14
|
+
/** The locale this manifest applies to (e.g. `"en-US"`). */
|
|
15
|
+
locale: string;
|
|
16
|
+
/** Campaigns and customer groups referenced across all variations in this manifest. */
|
|
17
|
+
context: PageManifestContext;
|
|
18
|
+
/** Ordered list of variation IDs defining the evaluation sequence. */
|
|
19
|
+
variationOrder: string[];
|
|
20
|
+
/** Map of variation ID to its entry data. */
|
|
21
|
+
variations: Record<string, VariationEntry>;
|
|
22
|
+
/** The variation ID to use when no other variation's rule matches. */
|
|
23
|
+
defaultVariation: string;
|
|
24
|
+
/**
|
|
25
|
+
* Component visibility rule definitions extracted from the page layout.
|
|
26
|
+
* Maps each component ID to its array of rule objects and a flag indicating
|
|
27
|
+
* if any rules are defined for that component.
|
|
28
|
+
*/
|
|
29
|
+
componentInfo: {
|
|
30
|
+
[componentId: string]: {
|
|
31
|
+
/** The visibility rules for this component. */
|
|
32
|
+
visibilityRules: VisibilityRuleDef[];
|
|
33
|
+
/** Whether this component or any of its descendants have visibility rules. */
|
|
34
|
+
hasAnyDescendantVisibilityRules: boolean;
|
|
35
|
+
/** Whether this component or any of its descendants has data bindings. */
|
|
36
|
+
hasAnyDescendantDataBindings: boolean;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Site-wide manifest containing content assignments that map product and category
|
|
42
|
+
* identifiers to page IDs, plus the category hierarchy used for parent-category
|
|
43
|
+
* traversal during lookup.
|
|
44
|
+
*/
|
|
45
|
+
interface SiteManifest {
|
|
46
|
+
/**
|
|
47
|
+
* Nested mapping of content assignments.
|
|
48
|
+
* Structure: `aspectType -> objectType -> objectId -> assignment`.
|
|
49
|
+
*
|
|
50
|
+
* For example, a PDP assignment for product "nike-air-max-90":
|
|
51
|
+
* `contentObjectAssignments.pdp.product["nike-air-max-90"].contentId`
|
|
52
|
+
*/
|
|
53
|
+
contentObjectAssignments: {
|
|
54
|
+
[aspectType: string]: {
|
|
55
|
+
[objectType: string]: {
|
|
56
|
+
[objectId: string]: {
|
|
57
|
+
/** Whether this assignment was explicitly set or inherited from a parent category. */
|
|
58
|
+
lookupMode: 'category-implicit' | 'category-explicit';
|
|
59
|
+
/** The page ID assigned to this object. */
|
|
60
|
+
contentId: string;
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Category hierarchy used to traverse from child to parent when resolving
|
|
67
|
+
* category-based content assignments.
|
|
68
|
+
*/
|
|
69
|
+
categories: {
|
|
70
|
+
[categoryId: string]: {
|
|
71
|
+
/** Display name of the category. */
|
|
72
|
+
name: string;
|
|
73
|
+
/** ID of the parent category, or undefined for root categories. */
|
|
74
|
+
parentCategory?: string;
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* A campaign and promotion pair used in visibility rules. Both the campaign and
|
|
80
|
+
* the specific promotion within it must be active in the shopper's context for
|
|
81
|
+
* the qualifier to match.
|
|
82
|
+
*/
|
|
83
|
+
interface CampaignQualifier {
|
|
84
|
+
/** The campaign identifier. */
|
|
85
|
+
campaignId: string;
|
|
86
|
+
/** The promotion identifier within the campaign. */
|
|
87
|
+
promotionId: string;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Metadata extracted from all variation rules in a {@link PageManifest}. Lists
|
|
91
|
+
* every campaign qualifier and customer group referenced, so the runtime knows
|
|
92
|
+
* which context values may be needed without inspecting each rule individually.
|
|
93
|
+
*/
|
|
94
|
+
interface PageManifestContext {
|
|
95
|
+
/** All campaign/promotion pairs referenced by any variation's visibility rule. */
|
|
96
|
+
campaignQualifiers: CampaignQualifier[];
|
|
97
|
+
/** All customer group IDs referenced by any variation's visibility rule. */
|
|
98
|
+
customerGroups: string[];
|
|
99
|
+
/** All data bindings required by components on this page, hoisted for batch resolution. */
|
|
100
|
+
dataBindings: DataBindingRequirement[];
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* A single data binding requirement declared by a component. The `type`
|
|
104
|
+
* identifies the data provider (e.g. `"content_asset"`, `"product"`) and the
|
|
105
|
+
* `id` identifies the specific record within that provider.
|
|
106
|
+
*
|
|
107
|
+
* These requirements are hoisted into {@link PageManifestContext} so MRT can
|
|
108
|
+
* request all required external data in a single batch during context resolution.
|
|
109
|
+
*/
|
|
110
|
+
interface DataBindingRequirement {
|
|
111
|
+
/** The data provider type (e.g. `"content_asset"`, `"product"`, `"personalization"`). */
|
|
112
|
+
type: string;
|
|
113
|
+
/** The UUID or identifier of the specific record. */
|
|
114
|
+
id: string;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* A single page variation within a {@link PageManifest}. Each variation holds
|
|
118
|
+
* the full page data and flags indicating whether qualifier context is needed
|
|
119
|
+
* for selection or component-level processing.
|
|
120
|
+
*/
|
|
121
|
+
interface VariationEntry {
|
|
122
|
+
/**
|
|
123
|
+
* Whether this variation's page contains components with visibility rules
|
|
124
|
+
* that require qualifier context. When `true`, the context resolver is called
|
|
125
|
+
* before processing the page's components.
|
|
126
|
+
*/
|
|
127
|
+
pageRequiresContext: boolean;
|
|
128
|
+
/**
|
|
129
|
+
* Whether this variation's own visibility rule requires qualifier context
|
|
130
|
+
* to evaluate. When `true`, the context resolver is called before checking
|
|
131
|
+
* the variation-level rule.
|
|
132
|
+
*/
|
|
133
|
+
ruleRequiresContext: boolean;
|
|
134
|
+
/** The visibility rule that must pass for this variation to be selected. Undefined for the default variation. */
|
|
135
|
+
visibilityRule?: VisibilityRuleDef;
|
|
136
|
+
/** The full page data for this variation. */
|
|
137
|
+
page: ShopperExperience.schemas['Page'];
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* A visibility rule definition that controls when a page variation or component
|
|
141
|
+
* is shown. All conditions within a rule use AND logic — every specified
|
|
142
|
+
* condition must pass for the rule to be satisfied.
|
|
143
|
+
*/
|
|
144
|
+
interface VisibilityRuleDef {
|
|
145
|
+
/** Customer groups that the shopper must belong to. All groups must match. */
|
|
146
|
+
customerGroups?: string[];
|
|
147
|
+
/** Campaign/promotion pairs that must be active. All qualifiers must match. */
|
|
148
|
+
campaignQualifiers?: CampaignQualifier[];
|
|
149
|
+
/** Time window during which the rule is active, as ISO 8601 UTC strings. */
|
|
150
|
+
schedule?: {
|
|
151
|
+
/** Start time as an ISO 8601 UTC string. Rule fails before this time. */
|
|
152
|
+
start?: string;
|
|
153
|
+
/** End time as an ISO 8601 UTC string. Rule fails after this time. */
|
|
154
|
+
end?: string;
|
|
155
|
+
};
|
|
156
|
+
/** Whether this rule is active for the locale of the manifest. */
|
|
157
|
+
isActiveForLocale: boolean;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Runtime context representing the current shopper's active qualifiers.
|
|
161
|
+
* Passed to {@link validateRule} to evaluate visibility rules. This context
|
|
162
|
+
* is typically resolved lazily — only fetched when a rule actually needs it.
|
|
163
|
+
*/
|
|
164
|
+
interface QualifierContext {
|
|
165
|
+
/**
|
|
166
|
+
* Active campaign qualifiers. Outer key is campaign ID, inner key is
|
|
167
|
+
* promotion ID. A value of `true` means the qualifier is active.
|
|
168
|
+
*/
|
|
169
|
+
campaignQualifiers: {
|
|
170
|
+
[campaignId: string]: {
|
|
171
|
+
[promotionId: string]: boolean;
|
|
172
|
+
};
|
|
173
|
+
};
|
|
174
|
+
/**
|
|
175
|
+
* Customer group memberships. Key is the customer group ID, value of
|
|
176
|
+
* `true` means the shopper belongs to that group.
|
|
177
|
+
*/
|
|
178
|
+
customerGroups: {
|
|
179
|
+
[customerGroupId: string]: boolean;
|
|
180
|
+
};
|
|
181
|
+
/**
|
|
182
|
+
* Resolved data binding objects returned from context resolution. Grouped
|
|
183
|
+
* by provider type, then by record ID. The `ExpressionResolver` uses this
|
|
184
|
+
* to evaluate attribute expressions like `content_asset.body`.
|
|
185
|
+
*/
|
|
186
|
+
dataBindings?: {
|
|
187
|
+
[type: string]: {
|
|
188
|
+
[id: string]: ResolvedDataBinding;
|
|
189
|
+
};
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* A resolved data binding object containing the fields returned by the data
|
|
194
|
+
* provider for a specific record. For example, a resolved `content_asset`
|
|
195
|
+
* might contain `{ title: "Winter Sale", body: "<div>…</div>" }`.
|
|
196
|
+
*/
|
|
197
|
+
interface ResolvedDataBinding {
|
|
198
|
+
[field: string]: unknown;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* The type of identifier used to look up a page. Determines how the ID is
|
|
202
|
+
* resolved to a page manifest:
|
|
203
|
+
* - `'page'` — Direct page ID, used as-is
|
|
204
|
+
* - `'category'` — Category ID, resolved via content assignments with parent traversal
|
|
205
|
+
* - `'product'` — Product ID, resolved via content assignments
|
|
206
|
+
*/
|
|
207
|
+
type IdentifierType = 'page' | 'category' | 'product';
|
|
208
|
+
/**
|
|
209
|
+
* Storage interface for fetching page and site manifests. Implementations
|
|
210
|
+
* decouple the page resolution logic from the underlying data source (e.g.,
|
|
211
|
+
* filesystem, CDN, database).
|
|
212
|
+
*/
|
|
213
|
+
interface ManifestStorage {
|
|
214
|
+
/** Fetch the page manifest for a given page ID and locale. */
|
|
215
|
+
getPageManifest(id: string, locale: string): Promise<PageManifest>;
|
|
216
|
+
/** Fetch the site-wide manifest for a given locale. */
|
|
217
|
+
getSiteManifest(locale: string): Promise<SiteManifest>;
|
|
218
|
+
}
|
|
219
|
+
type VisitorContextType = 'page' | 'region' | 'component' | 'root';
|
|
220
|
+
type InferNodeFromType<TType extends VisitorContextType> = TType extends 'page' ? ShopperExperience.schemas['Page'] : TType extends 'region' ? ShopperExperience.schemas['Region'] : ShopperExperience.schemas['Component'];
|
|
221
|
+
//#endregion
|
|
222
|
+
//#region src/design/data/page/process-page.d.ts
|
|
223
|
+
/**
|
|
224
|
+
* Context required for page processing. Contains the shopper's runtime
|
|
225
|
+
* qualifiers and the component-level visibility rules from the page manifest.
|
|
226
|
+
*/
|
|
227
|
+
interface PageProcessorContext {
|
|
228
|
+
/** The shopper's active qualifiers (campaigns, customer groups), or `null` if not resolved. */
|
|
229
|
+
qualifiers: QualifierContext | null;
|
|
230
|
+
/** Component visibility rule definitions extracted from the page layout. */
|
|
231
|
+
componentInfo: PageManifest['componentInfo'];
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Filters a page's components based on their visibility rules and resolves
|
|
235
|
+
* data binding expressions in a single traversal. Traverses the page tree
|
|
236
|
+
* using the visitor pattern and:
|
|
237
|
+
*
|
|
238
|
+
* 1. Removes any component whose visibility rules do not pass against the
|
|
239
|
+
* shopper's qualifier context.
|
|
240
|
+
* 2. Resolves data binding expressions in each surviving component's `data`
|
|
241
|
+
* attributes using the resolved data bindings from context resolution.
|
|
242
|
+
*
|
|
243
|
+
* A component is visible if **any** of its visibility rules pass (OR logic).
|
|
244
|
+
* If a component has rules and none of them pass, it is removed. Components
|
|
245
|
+
* without rules are always included.
|
|
246
|
+
*
|
|
247
|
+
* @param page - The page to process.
|
|
248
|
+
* @param context - The processing context with qualifier data, visibility rules, and resolved data bindings.
|
|
249
|
+
* @returns A new page with invisible components filtered out and data binding expressions resolved.
|
|
250
|
+
*
|
|
251
|
+
* @example
|
|
252
|
+
* ```ts
|
|
253
|
+
* import { processPage } from '@salesforce/storefront-next-runtime/design/data';
|
|
254
|
+
*
|
|
255
|
+
* const page = {
|
|
256
|
+
* id: 'homepage',
|
|
257
|
+
* typeId: 'storePage',
|
|
258
|
+
* regions: [{
|
|
259
|
+
* id: 'main',
|
|
260
|
+
* components: [
|
|
261
|
+
* { id: 'public-banner', typeId: 'commerce_assets.heroBanner', regions: [] },
|
|
262
|
+
* { id: 'loyalty-offer', typeId: 'commerce_assets.promoTile', regions: [] },
|
|
263
|
+
* ],
|
|
264
|
+
* }],
|
|
265
|
+
* };
|
|
266
|
+
*
|
|
267
|
+
* // The "loyalty-offer" component requires the shopper to be in "loyalty-members"
|
|
268
|
+
* const componentInfo = {
|
|
269
|
+
* 'public-banner': { visibilityRules: [], hasVisibilityRules: false },
|
|
270
|
+
* 'loyalty-offer': {
|
|
271
|
+
* visibilityRules: [{ customerGroups: ['loyalty-members'] }],
|
|
272
|
+
* hasVisibilityRules: true,
|
|
273
|
+
* },
|
|
274
|
+
* };
|
|
275
|
+
*
|
|
276
|
+
* // Guest shopper — not in any customer group
|
|
277
|
+
* const filtered = processPage(page, {
|
|
278
|
+
* qualifiers: { customerGroups: {}, campaignQualifiers: {} },
|
|
279
|
+
* componentInfo,
|
|
280
|
+
* });
|
|
281
|
+
* // filtered.regions[0].components has only "public-banner"
|
|
282
|
+
* // "loyalty-offer" was removed because the shopper isn't a loyalty member
|
|
283
|
+
* ```
|
|
284
|
+
*/
|
|
285
|
+
declare function processPage(page: ShopperExperience.schemas['Page'], processorContext: PageProcessorContext): ShopperExperience.schemas['Page'];
|
|
286
|
+
//#endregion
|
|
287
|
+
//#region src/design/data/page/transform.d.ts
|
|
288
|
+
/**
|
|
289
|
+
* Context object passed to {@link PageVisitor} handler methods during page tree
|
|
290
|
+
* traversal. Provides access to the current node via {@link node}, the tree
|
|
291
|
+
* position via {@link page}, {@link parentRegion}, and {@link parentComponent},
|
|
292
|
+
* and traversal methods ({@link visitRegions}, {@link visitComponents}) for
|
|
293
|
+
* continuing into child nodes.
|
|
294
|
+
*
|
|
295
|
+
* When a visitor handler is defined, the handler is responsible for traversing
|
|
296
|
+
* into children by calling the appropriate context method. If the handler does
|
|
297
|
+
* not call these methods, children will not be visited.
|
|
298
|
+
*/
|
|
299
|
+
declare class VisitorContext<TNode> {
|
|
300
|
+
private readonly context;
|
|
301
|
+
constructor(context: {
|
|
302
|
+
/** The current node being visited. */
|
|
303
|
+
node: TNode;
|
|
304
|
+
/** The node type */
|
|
305
|
+
type: VisitorContextType;
|
|
306
|
+
/** The visitor being used to transform the page tree. */
|
|
307
|
+
visitor: PageVisitor;
|
|
308
|
+
/** The root page being traversed. */
|
|
309
|
+
page?: ShopperExperience.schemas['Page'];
|
|
310
|
+
/** The parent region of the current node, if traversing within a region. */
|
|
311
|
+
parentRegion?: ShopperExperience.schemas['Region'];
|
|
312
|
+
/** The parent component of the current node, if traversing within a component's nested regions. */
|
|
313
|
+
parentComponent?: ShopperExperience.schemas['Component'];
|
|
314
|
+
});
|
|
315
|
+
get type(): VisitorContextType;
|
|
316
|
+
/**
|
|
317
|
+
* The current node being visited.
|
|
318
|
+
*/
|
|
319
|
+
get node(): TNode;
|
|
320
|
+
/**
|
|
321
|
+
* The root page being traversed.
|
|
322
|
+
*/
|
|
323
|
+
get page(): ShopperExperience.schemas['Page'] | undefined;
|
|
324
|
+
/**
|
|
325
|
+
* The parent region of the current node, if traversing within a region.
|
|
326
|
+
*/
|
|
327
|
+
get parentRegion(): ShopperExperience.schemas['Region'] | undefined;
|
|
328
|
+
/**
|
|
329
|
+
* The parent component of the current node, if traversing within a component's nested regions.
|
|
330
|
+
*/
|
|
331
|
+
get parentComponent(): ShopperExperience.schemas['Component'] | undefined;
|
|
332
|
+
/**
|
|
333
|
+
* Traverses an array of regions, invoking the visitor's `visitRegion` handler
|
|
334
|
+
* on each one. Regions for which the handler returns `null` are excluded from
|
|
335
|
+
* the result. Call this from within a `visitPage` or `visitComponent` handler
|
|
336
|
+
* to continue traversal into child regions.
|
|
337
|
+
*
|
|
338
|
+
* @param regions - The regions to traverse.
|
|
339
|
+
* @returns The filtered array of transformed regions.
|
|
340
|
+
*
|
|
341
|
+
* @example
|
|
342
|
+
* ```ts
|
|
343
|
+
* transformPage(page, {
|
|
344
|
+
* visitPage(context) {
|
|
345
|
+
* // Traverse into regions explicitly
|
|
346
|
+
* const regions = context.visitRegions(context.node.regions);
|
|
347
|
+
* return { ...context.node, regions };
|
|
348
|
+
* },
|
|
349
|
+
* });
|
|
350
|
+
* ```
|
|
351
|
+
*/
|
|
352
|
+
visitRegions(regions?: ShopperExperience.schemas['Region'][]): ShopperExperience.schemas['Region'][];
|
|
353
|
+
/**
|
|
354
|
+
* Traverses a single region. If the visitor has a `visitRegion` handler, the
|
|
355
|
+
* handler is called with a new {@link VisitorContext} for the region. Otherwise,
|
|
356
|
+
* the region's child components are traversed automatically.
|
|
357
|
+
*
|
|
358
|
+
* @param region - The region to visit.
|
|
359
|
+
* @returns The transformed region, or `null` to exclude it.
|
|
360
|
+
*/
|
|
361
|
+
visitRegion(region: ShopperExperience.schemas['Region']): ShopperExperience.schemas['Region'] | null;
|
|
362
|
+
/**
|
|
363
|
+
* Traverses an array of components, invoking the visitor's `visitComponent`
|
|
364
|
+
* handler on each one. Components for which the handler returns `null` are
|
|
365
|
+
* excluded from the result. Call this from within a `visitRegion` handler to
|
|
366
|
+
* continue traversal into child components.
|
|
367
|
+
*
|
|
368
|
+
* @param components - The components to traverse.
|
|
369
|
+
* @returns The filtered array of transformed components.
|
|
370
|
+
*
|
|
371
|
+
* @example
|
|
372
|
+
* ```ts
|
|
373
|
+
* transformPage(page, {
|
|
374
|
+
* visitRegion(context) {
|
|
375
|
+
* // Traverse into components explicitly
|
|
376
|
+
* const components = context.visitComponents(context.node.components);
|
|
377
|
+
* return { ...context.node, components };
|
|
378
|
+
* },
|
|
379
|
+
* });
|
|
380
|
+
* ```
|
|
381
|
+
*/
|
|
382
|
+
visitComponents(components?: ShopperExperience.schemas['Component'][]): ShopperExperience.schemas['Component'][];
|
|
383
|
+
/**
|
|
384
|
+
* Traverses a single component. If the visitor has a `visitComponent` handler,
|
|
385
|
+
* the handler is called with a new {@link VisitorContext} for the component.
|
|
386
|
+
* Otherwise, the component's nested regions are traversed automatically.
|
|
387
|
+
*
|
|
388
|
+
* @param component - The component to visit.
|
|
389
|
+
* @returns The transformed component, or `null` to exclude it.
|
|
390
|
+
*/
|
|
391
|
+
visitComponent(component: ShopperExperience.schemas['Component']): ShopperExperience.schemas['Component'] | null;
|
|
392
|
+
/**
|
|
393
|
+
* Traverses a single page. If the visitor has a `visitPage` handler, the
|
|
394
|
+
* handler is called with a new {@link VisitorContext} for the page. Otherwise,
|
|
395
|
+
* the page's regions are traversed automatically.
|
|
396
|
+
*
|
|
397
|
+
* @param page - The page to visit.
|
|
398
|
+
* @returns The transformed page, or `null` to exclude it.
|
|
399
|
+
*/
|
|
400
|
+
visitPage(page: ShopperExperience.schemas['Page']): ShopperExperience.schemas['Page'] | null;
|
|
401
|
+
private toChildContext;
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Visitor interface for traversing and transforming a Page Designer page tree.
|
|
405
|
+
* Implement any combination of visit methods to intercept pages, regions, or
|
|
406
|
+
* components during traversal. Return `null` from `visitRegion` or
|
|
407
|
+
* `visitComponent` to remove that element from the tree.
|
|
408
|
+
*/
|
|
409
|
+
interface PageVisitor {
|
|
410
|
+
visitPage?(context: VisitorContext<ShopperExperience.schemas['Page']>): ShopperExperience.schemas['Page'];
|
|
411
|
+
visitRegion?(context: VisitorContext<ShopperExperience.schemas['Region']>): ShopperExperience.schemas['Region'] | null;
|
|
412
|
+
visitComponent?(component: VisitorContext<ShopperExperience.schemas['Component']>): ShopperExperience.schemas['Component'] | null;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Traverses a page tree using the visitor pattern, applying the visitor's
|
|
416
|
+
* callbacks to the page, its regions, and their nested components. This is
|
|
417
|
+
* the top-level entry point for page tree transformation.
|
|
418
|
+
*
|
|
419
|
+
* When a visitor handler is defined, it receives a {@link VisitorContext} and
|
|
420
|
+
* is responsible for traversing into children using the context's traversal
|
|
421
|
+
* methods (`visitRegions`, `visitComponents`). If the handler does not call
|
|
422
|
+
* these methods, children will not be visited. When no handler is defined for
|
|
423
|
+
* a node type, children are traversed automatically.
|
|
424
|
+
*
|
|
425
|
+
* Returning `null` from a `visitRegion` or `visitComponent` callback removes
|
|
426
|
+
* that element and its children from the resulting tree.
|
|
427
|
+
*
|
|
428
|
+
* @param page - The page to traverse.
|
|
429
|
+
* @param visitor - The visitor with callbacks to apply at each tree node.
|
|
430
|
+
* @returns A new page with visitor transformations applied, or `null`.
|
|
431
|
+
*
|
|
432
|
+
* @example
|
|
433
|
+
* ```ts
|
|
434
|
+
* import { transformPage } from '@salesforce/storefront-next-runtime/design/data';
|
|
435
|
+
*
|
|
436
|
+
* const page = { id: 'homepage', typeId: 'storePage', regions: [
|
|
437
|
+
* { id: 'header', components: [
|
|
438
|
+
* { id: 'hero-banner', typeId: 'commerce_assets.heroBanner', regions: [] },
|
|
439
|
+
* { id: 'promo-tile', typeId: 'commerce_assets.promoTile', regions: [] },
|
|
440
|
+
* ]},
|
|
441
|
+
* ]};
|
|
442
|
+
*
|
|
443
|
+
* // When only visitComponent is defined, regions are traversed automatically.
|
|
444
|
+
* // The handler receives a VisitorContext — use context.node to access the component.
|
|
445
|
+
* transformPage(page, {
|
|
446
|
+
* visitComponent(context) {
|
|
447
|
+
* console.log(`Component: ${context.node.typeId} in region ${context.parentRegion?.id}`);
|
|
448
|
+
* return context.node;
|
|
449
|
+
* },
|
|
450
|
+
* });
|
|
451
|
+
*
|
|
452
|
+
* // When visitRegion is defined, the handler must traverse into children explicitly.
|
|
453
|
+
* // Without calling context.visitComponents(), components inside the region are skipped.
|
|
454
|
+
* transformPage(page, {
|
|
455
|
+
* visitRegion(context) {
|
|
456
|
+
* console.log(`Entering region: ${context.node.id}`);
|
|
457
|
+
* const components = context.visitComponents(context.node.components);
|
|
458
|
+
* return { ...context.node, components };
|
|
459
|
+
* },
|
|
460
|
+
* visitComponent(context) {
|
|
461
|
+
* console.log(` Component: ${context.node.typeId}`);
|
|
462
|
+
* return context.node;
|
|
463
|
+
* },
|
|
464
|
+
* });
|
|
465
|
+
* ```
|
|
466
|
+
*/
|
|
467
|
+
declare function transformPage(page: ShopperExperience.schemas['Page'], visitor: PageVisitor): ShopperExperience.schemas['Page'] | null;
|
|
468
|
+
/**
|
|
469
|
+
* Applies the visitor to a single component. If the visitor's `visitComponent`
|
|
470
|
+
* handler is defined, it receives a {@link VisitorContext} and is responsible
|
|
471
|
+
* for traversing into the component's nested regions using `context.visitRegions()`.
|
|
472
|
+
* If no `visitComponent` handler is defined, nested regions are traversed
|
|
473
|
+
* automatically. Returns `null` to exclude the component from the result.
|
|
474
|
+
*
|
|
475
|
+
* @param component - The component to transform.
|
|
476
|
+
* @param visitor - The visitor with callbacks.
|
|
477
|
+
* @returns The transformed component, or `null` to exclude it.
|
|
478
|
+
*
|
|
479
|
+
* @example
|
|
480
|
+
* ```ts
|
|
481
|
+
* import { transformComponent } from '@salesforce/storefront-next-runtime/design/data';
|
|
482
|
+
*
|
|
483
|
+
* // Replace the image URL in a hero banner component and traverse its nested regions
|
|
484
|
+
* const heroBanner = {
|
|
485
|
+
* id: 'hero-1',
|
|
486
|
+
* typeId: 'commerce_assets.heroBanner',
|
|
487
|
+
* data: { imageUrl: '/images/summer-sale.jpg' },
|
|
488
|
+
* regions: [{ id: 'banner-content', components: [] }],
|
|
489
|
+
* };
|
|
490
|
+
*
|
|
491
|
+
* const result = transformComponent(heroBanner, {
|
|
492
|
+
* visitComponent(context) {
|
|
493
|
+
* // Traverse into nested regions using the context API
|
|
494
|
+
* const regions = context.visitRegions(context.node.regions);
|
|
495
|
+
*
|
|
496
|
+
* if (context.node.typeId === 'commerce_assets.heroBanner') {
|
|
497
|
+
* return { ...context.node, regions, data: { ...context.node.data, imageUrl: '/images/winter-sale.jpg' } };
|
|
498
|
+
* }
|
|
499
|
+
* return { ...context.node, regions };
|
|
500
|
+
* },
|
|
501
|
+
* });
|
|
502
|
+
* ```
|
|
503
|
+
*/
|
|
504
|
+
declare function transformComponent(component: ShopperExperience.schemas['Component'], visitor: PageVisitor): ShopperExperience.schemas['Component'] | null;
|
|
505
|
+
/**
|
|
506
|
+
* Applies the visitor to a single region. If the visitor's `visitRegion`
|
|
507
|
+
* handler is defined, it receives a {@link VisitorContext} and is responsible
|
|
508
|
+
* for traversing into the region's child components using `context.visitComponents()`.
|
|
509
|
+
* If no `visitRegion` handler is defined, child components are traversed
|
|
510
|
+
* automatically. Returns `null` to exclude the region and all its children
|
|
511
|
+
* from the result.
|
|
512
|
+
*
|
|
513
|
+
* @param region - The region to transform.
|
|
514
|
+
* @param visitor - The visitor with callbacks.
|
|
515
|
+
* @returns The transformed region, or `null` to exclude it.
|
|
516
|
+
*
|
|
517
|
+
* @example
|
|
518
|
+
* ```ts
|
|
519
|
+
* import { transformRegion } from '@salesforce/storefront-next-runtime/design/data';
|
|
520
|
+
*
|
|
521
|
+
* // Filter empty regions and traverse into non-empty ones
|
|
522
|
+
* const emptyRegion = { id: 'sidebar', components: [] };
|
|
523
|
+
* const populatedRegion = { id: 'main', components: [
|
|
524
|
+
* { id: 'product-grid', typeId: 'commerce_assets.productGrid', regions: [] },
|
|
525
|
+
* ]};
|
|
526
|
+
*
|
|
527
|
+
* const visitor = {
|
|
528
|
+
* visitRegion(context) {
|
|
529
|
+
* if (!context.node.components?.length) {
|
|
530
|
+
* return null; // Remove empty regions
|
|
531
|
+
* }
|
|
532
|
+
* // Traverse into child components using the context API
|
|
533
|
+
* const components = context.visitComponents(context.node.components);
|
|
534
|
+
* return { ...context.node, components };
|
|
535
|
+
* },
|
|
536
|
+
* };
|
|
537
|
+
*
|
|
538
|
+
* transformRegion(emptyRegion, visitor); // => null (removed)
|
|
539
|
+
* transformRegion(populatedRegion, visitor); // => { id: 'main', components: [...] }
|
|
540
|
+
* ```
|
|
541
|
+
*/
|
|
542
|
+
declare function transformRegion(region: ShopperExperience.schemas['Region'], visitor: PageVisitor): ShopperExperience.schemas['Region'] | null;
|
|
543
|
+
//#endregion
|
|
544
|
+
//#region src/design/data/errors/required.d.ts
|
|
545
|
+
/**
|
|
546
|
+
* Copyright 2026 Salesforce, Inc.
|
|
547
|
+
*
|
|
548
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
549
|
+
* you may not use this file except in compliance with the License.
|
|
550
|
+
* You may obtain a copy of the License at
|
|
551
|
+
*
|
|
552
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
553
|
+
*
|
|
554
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
555
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
556
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
557
|
+
* See the License for the specific language governing permissions and
|
|
558
|
+
* limitations under the License.
|
|
559
|
+
*/
|
|
560
|
+
declare class RequiredError extends Error {
|
|
561
|
+
constructor(message: string);
|
|
562
|
+
static assert<TValue>(value: TValue, message: string, isEmpty?: (value: TValue) => boolean): asserts value is NonNullable<TValue>;
|
|
563
|
+
}
|
|
564
|
+
//#endregion
|
|
565
|
+
//#region src/design/data/page/resolve-data-bindings.d.ts
|
|
566
|
+
/**
|
|
567
|
+
* Data binding metadata attached to a component instance. Stored in the
|
|
568
|
+
* component's `custom.dataBinding` field by ECOM when the author binds a
|
|
569
|
+
* data source to the component in Page Designer.
|
|
570
|
+
*/
|
|
571
|
+
interface ComponentDataBinding {
|
|
572
|
+
/** Maps attribute names to expression strings (e.g. `"content_asset.body"`). */
|
|
573
|
+
expressions: Record<string, string>;
|
|
574
|
+
/** The data contexts bound to this component, identifying the records to resolve against. */
|
|
575
|
+
contexts: DataBindingContext[];
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* A data context reference on a component instance, identifying a specific
|
|
579
|
+
* record from a data provider.
|
|
580
|
+
*/
|
|
581
|
+
interface DataBindingContext {
|
|
582
|
+
/** The data provider type (e.g. `"content_asset"`). */
|
|
583
|
+
type: string;
|
|
584
|
+
/** The record identifier (e.g. a content asset UUID). */
|
|
585
|
+
id: string;
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* Parses a binding expression string into its provider type and field name.
|
|
589
|
+
* Supports the bare `type.field` format.
|
|
590
|
+
*
|
|
591
|
+
* @param expression - The expression string to parse.
|
|
592
|
+
* @returns The parsed type and field, or `null` if the expression is invalid.
|
|
593
|
+
*
|
|
594
|
+
* @example
|
|
595
|
+
* ```ts
|
|
596
|
+
* parseExpression('content_asset.title'); // { type: 'content_asset', field: 'title' }
|
|
597
|
+
* parseExpression('invalid'); // null
|
|
598
|
+
* ```
|
|
599
|
+
*/
|
|
600
|
+
declare function parseExpression(expression: string): {
|
|
601
|
+
type: string;
|
|
602
|
+
field: string;
|
|
603
|
+
} | null;
|
|
604
|
+
/**
|
|
605
|
+
* Resolves a single binding expression against the component's data contexts
|
|
606
|
+
* and the resolved data bindings from context resolution.
|
|
607
|
+
*
|
|
608
|
+
* Returns the resolved field value, or an empty string if the expression is
|
|
609
|
+
* invalid, the matching context or record is not found, or the field does not
|
|
610
|
+
* exist on the resolved record.
|
|
611
|
+
*
|
|
612
|
+
* @param expression - The expression string (e.g. `"content_asset.body"`).
|
|
613
|
+
* @param contexts - The component's data binding contexts.
|
|
614
|
+
* @param dataBindings - The resolved data bindings from {@link QualifierContext}.
|
|
615
|
+
* @returns The resolved value, or `''` if resolution fails.
|
|
616
|
+
*/
|
|
617
|
+
declare function resolveExpression(expression: string, contexts: DataBindingContext[], dataBindings: NonNullable<QualifierContext['dataBindings']>): unknown;
|
|
618
|
+
/**
|
|
619
|
+
* Resolves data binding expressions for a single component. Replaces attribute
|
|
620
|
+
* values in the component's `data` with the resolved values from context
|
|
621
|
+
* resolution. Attributes without a matching expression are preserved as-is.
|
|
622
|
+
* When an expression cannot be resolved, the attribute value is set to an
|
|
623
|
+
* empty string.
|
|
624
|
+
*
|
|
625
|
+
* Returns the component unchanged if it has no data binding metadata or if
|
|
626
|
+
* `dataBindings` is `undefined`.
|
|
627
|
+
*
|
|
628
|
+
* @param component - The component to resolve data bindings for.
|
|
629
|
+
* @param dataBindings - The resolved data bindings from {@link QualifierContext}, or `undefined` if no bindings were resolved.
|
|
630
|
+
* @returns The component with resolved attribute values, or the original component if no bindings apply.
|
|
631
|
+
*
|
|
632
|
+
* @example
|
|
633
|
+
* ```ts
|
|
634
|
+
* import { resolveComponentDataBindings } from '@salesforce/storefront-next-runtime/design/data';
|
|
635
|
+
*
|
|
636
|
+
* const component = {
|
|
637
|
+
* id: 'banner',
|
|
638
|
+
* typeId: 'commerce_assets.contentBanner',
|
|
639
|
+
* data: { heading: 'Fallback Title', body: 'Fallback Body' },
|
|
640
|
+
* custom: {
|
|
641
|
+
* dataBinding: {
|
|
642
|
+
* expressions: {
|
|
643
|
+
* heading: 'content_asset.title',
|
|
644
|
+
* body: 'content_asset.body',
|
|
645
|
+
* },
|
|
646
|
+
* contexts: [{ type: 'content_asset', id: 'winter-sale-uuid' }],
|
|
647
|
+
* },
|
|
648
|
+
* },
|
|
649
|
+
* regions: [],
|
|
650
|
+
* };
|
|
651
|
+
*
|
|
652
|
+
* const dataBindings = {
|
|
653
|
+
* content_asset: {
|
|
654
|
+
* 'winter-sale-uuid': {
|
|
655
|
+
* title: 'Winter Sale',
|
|
656
|
+
* body: '<div>Free Shipping on all orders!</div>',
|
|
657
|
+
* },
|
|
658
|
+
* },
|
|
659
|
+
* };
|
|
660
|
+
*
|
|
661
|
+
* const resolved = resolveComponentDataBindings(component, dataBindings);
|
|
662
|
+
* // resolved.data.heading === 'Winter Sale'
|
|
663
|
+
* // resolved.data.body === '<div>Free Shipping on all orders!</div>'
|
|
664
|
+
* ```
|
|
665
|
+
*/
|
|
666
|
+
declare function resolveComponentDataBindings(component: ShopperExperience.schemas['Component'], dataBindings: QualifierContext['dataBindings']): ShopperExperience.schemas['Component'];
|
|
667
|
+
//#endregion
|
|
668
|
+
//#region src/design/data/page/resolve-page.d.ts
|
|
669
|
+
/**
|
|
670
|
+
* Main entry point for the page resolution pipeline. Orchestrates the full flow:
|
|
671
|
+
*
|
|
672
|
+
* 1. **Resolve dynamic page ID** — For product/category identifiers, looks up
|
|
673
|
+
* the assigned page ID via content assignments in the site manifest.
|
|
674
|
+
* 2. **Fetch page manifest** — Loads all variations for the resolved page.
|
|
675
|
+
* 3. **Select variation** — Evaluates visibility rules to pick the right variation.
|
|
676
|
+
* 4. **Load qualifier context** — Lazily fetches the shopper's context only if needed.
|
|
677
|
+
* 5. **Process page** — Filters out components that fail visibility rules.
|
|
678
|
+
*
|
|
679
|
+
* Returns `null` if the page ID cannot be resolved, the manifest doesn't exist,
|
|
680
|
+
* or no variation is available.
|
|
681
|
+
*
|
|
682
|
+
* @param options - The resolution options.
|
|
683
|
+
* @param options.id - The identifier to resolve (product ID, category ID, or page ID).
|
|
684
|
+
* @param options.identifierType - The type of identifier: `'product'`, `'category'`, or `'page'`.
|
|
685
|
+
* @param options.locale - The locale to resolve the page for (e.g. `"en-US"`).
|
|
686
|
+
* @param options.manifestStorage - Storage implementation for fetching manifests.
|
|
687
|
+
* @param options.contextResolver - Optional async function that returns the shopper's qualifier context. Only called if a visibility rule needs it.
|
|
688
|
+
* @param options.aspectType - The aspect type to resolve the page for when the identifier type is `'product'` or `'category'`.
|
|
689
|
+
* @returns The fully resolved and filtered page, or `null`.
|
|
690
|
+
*
|
|
691
|
+
* @example
|
|
692
|
+
* ```ts
|
|
693
|
+
* import { resolvePage } from '@salesforce/storefront-next-runtime/design/data';
|
|
694
|
+
*
|
|
695
|
+
* // Resolve the PDP page for a specific product with an active holiday campaign
|
|
696
|
+
* const page = await resolvePage({
|
|
697
|
+
* id: 'nike-air-max-90',
|
|
698
|
+
* identifierType: 'product',
|
|
699
|
+
* aspectType: 'pdp',
|
|
700
|
+
* locale: 'en-US',
|
|
701
|
+
* manifestStorage: {
|
|
702
|
+
* async getPageManifest(id, locale) {
|
|
703
|
+
* // Fetch from CDN, filesystem, or database
|
|
704
|
+
* return fetchManifest(`/manifests/${locale}/${id}.json`);
|
|
705
|
+
* },
|
|
706
|
+
* async getSiteManifest(locale) {
|
|
707
|
+
* return fetchManifest(`/manifests/${locale}/site.json`);
|
|
708
|
+
* },
|
|
709
|
+
* },
|
|
710
|
+
* contextResolver: async () => ({
|
|
711
|
+
* customerGroups: { 'vip-customers': true },
|
|
712
|
+
* campaignQualifiers: {
|
|
713
|
+
* 'holiday-sale-2026': { 'free-shipping': true },
|
|
714
|
+
* },
|
|
715
|
+
* }),
|
|
716
|
+
* });
|
|
717
|
+
*
|
|
718
|
+
* if (page) {
|
|
719
|
+
* // page.regions contains only components visible to this VIP shopper
|
|
720
|
+
* // during the holiday sale campaign
|
|
721
|
+
* renderPage(page);
|
|
722
|
+
* }
|
|
723
|
+
* ```
|
|
724
|
+
*/
|
|
725
|
+
declare function resolvePage({
|
|
726
|
+
id,
|
|
727
|
+
identifierType,
|
|
728
|
+
aspectType,
|
|
729
|
+
locale,
|
|
730
|
+
manifestStorage,
|
|
731
|
+
contextResolver
|
|
732
|
+
}: {
|
|
733
|
+
id: string;
|
|
734
|
+
identifierType: IdentifierType;
|
|
735
|
+
aspectType?: string;
|
|
736
|
+
locale: string;
|
|
737
|
+
manifestStorage: ManifestStorage;
|
|
738
|
+
contextResolver?: () => Promise<QualifierContext>;
|
|
739
|
+
}): Promise<ShopperExperience.schemas['Page'] | null>;
|
|
740
|
+
//#endregion
|
|
741
|
+
//#region src/design/data/manifest/resolve-dynamic-page-id.d.ts
|
|
742
|
+
/**
|
|
743
|
+
* Converts a product or category identifier into a page ID by looking up
|
|
744
|
+
* content assignments in the site manifest. For categories, the lookup
|
|
745
|
+
* traverses the category hierarchy from the given category up to the root,
|
|
746
|
+
* returning the first matching assignment.
|
|
747
|
+
*
|
|
748
|
+
* Returns `null` if no content assignment is found for the identifier or if
|
|
749
|
+
* the identifier type has no registered resolver.
|
|
750
|
+
*
|
|
751
|
+
* @param options - The resolution options.
|
|
752
|
+
* @param options.id - The identifier to resolve (product ID, category ID, or page ID).
|
|
753
|
+
* @param options.identifierType - The type of identifier: `'product'`, `'category'`, or `'page'`.
|
|
754
|
+
* @param options.siteManifest - The site manifest containing content assignments and category hierarchy.
|
|
755
|
+
* @returns The resolved page ID, or `null` if no assignment was found.
|
|
756
|
+
*
|
|
757
|
+
* @example
|
|
758
|
+
* ```ts
|
|
759
|
+
* import { resolveDynamicPageId } from '@salesforce/storefront-next-runtime/design/data';
|
|
760
|
+
*
|
|
761
|
+
* const siteManifest = {
|
|
762
|
+
* contentObjectAssignments: {
|
|
763
|
+
* plp: {
|
|
764
|
+
* category: {
|
|
765
|
+
* 'mens-shoes': {
|
|
766
|
+
* lookupMode: 'category-explicit',
|
|
767
|
+
* contentId: 'page-mens-shoes-plp',
|
|
768
|
+
* },
|
|
769
|
+
* },
|
|
770
|
+
* },
|
|
771
|
+
* },
|
|
772
|
+
* categories: {
|
|
773
|
+
* 'mens-running-shoes': { name: 'Running Shoes', parentCategory: 'mens-shoes' },
|
|
774
|
+
* 'mens-shoes': { name: "Men's Shoes" },
|
|
775
|
+
* },
|
|
776
|
+
* };
|
|
777
|
+
*
|
|
778
|
+
* // Direct match
|
|
779
|
+
* resolveDynamicPageId({ id: 'mens-shoes', identifierType: 'category', siteManifest });
|
|
780
|
+
* // => 'page-mens-shoes-plp'
|
|
781
|
+
*
|
|
782
|
+
* // Inherited from parent category
|
|
783
|
+
* resolveDynamicPageId({ id: 'mens-running-shoes', identifierType: 'category', siteManifest });
|
|
784
|
+
* // => 'page-mens-shoes-plp' (found via parent traversal)
|
|
785
|
+
*
|
|
786
|
+
* // No assignment found
|
|
787
|
+
* resolveDynamicPageId({ id: 'womens-shoes', identifierType: 'category', siteManifest });
|
|
788
|
+
* // => null
|
|
789
|
+
* ```
|
|
790
|
+
*/
|
|
791
|
+
declare function resolveDynamicPageId<TIdentifier extends IdentifierType = IdentifierType>({
|
|
792
|
+
id,
|
|
793
|
+
identifierType,
|
|
794
|
+
siteManifest,
|
|
795
|
+
aspectType
|
|
796
|
+
}: {
|
|
797
|
+
id: string;
|
|
798
|
+
identifierType: TIdentifier;
|
|
799
|
+
aspectType: string;
|
|
800
|
+
siteManifest?: SiteManifest;
|
|
801
|
+
}): string | null;
|
|
802
|
+
//#endregion
|
|
803
|
+
//#region src/design/data/manifest/get-page.d.ts
|
|
804
|
+
/**
|
|
805
|
+
* Selects the appropriate page variation from a manifest by evaluating each
|
|
806
|
+
* variation's visibility rule in order. Returns the first variation whose rule
|
|
807
|
+
* passes, or falls back to the manifest's default variation.
|
|
808
|
+
*
|
|
809
|
+
* The qualifier context is resolved lazily — the `contextResolver` is only
|
|
810
|
+
* called when a variation's `ruleRequiresContext` flag is `true`, and only
|
|
811
|
+
* once (the result is cached for subsequent variations).
|
|
812
|
+
*
|
|
813
|
+
* @param manifest - The page manifest containing all variations.
|
|
814
|
+
* @param options - Resolution options.
|
|
815
|
+
* @param options.contextResolver - Optional async function that returns the shopper's qualifier context. Only called if a variation's rule needs it.
|
|
816
|
+
* @returns The selected variation entry and resolved context, or `null` if no variation (including default) exists.
|
|
817
|
+
*
|
|
818
|
+
* @example
|
|
819
|
+
* ```ts
|
|
820
|
+
* import { getPageFromManifest } from '@salesforce/storefront-next-runtime/design/data';
|
|
821
|
+
*
|
|
822
|
+
* const manifest = {
|
|
823
|
+
* pageId: 'homepage',
|
|
824
|
+
* locale: 'en-US',
|
|
825
|
+
* context: { campaignQualifiers: [], customerGroups: ['vip-customers'], dataBindings: [] },
|
|
826
|
+
* variationOrder: ['vip-homepage', 'holiday-homepage'],
|
|
827
|
+
* variations: {
|
|
828
|
+
* 'vip-homepage': {
|
|
829
|
+
* ruleRequiresContext: true,
|
|
830
|
+
* pageRequiresContext: false,
|
|
831
|
+
* visibilityRule: { customerGroups: ['vip-customers'] },
|
|
832
|
+
* page: { id: 'homepage', typeId: 'storePage', regions: [] },
|
|
833
|
+
* },
|
|
834
|
+
* 'holiday-homepage': {
|
|
835
|
+
* ruleRequiresContext: false,
|
|
836
|
+
* pageRequiresContext: false,
|
|
837
|
+
* visibilityRule: {
|
|
838
|
+
* schedule: {
|
|
839
|
+
* start: new Date('2026-12-01').getTime(),
|
|
840
|
+
* end: new Date('2026-12-31').getTime(),
|
|
841
|
+
* },
|
|
842
|
+
* },
|
|
843
|
+
* page: { id: 'homepage', typeId: 'storePage', regions: [] },
|
|
844
|
+
* },
|
|
845
|
+
* 'default-homepage': {
|
|
846
|
+
* ruleRequiresContext: false,
|
|
847
|
+
* pageRequiresContext: false,
|
|
848
|
+
* page: { id: 'homepage', typeId: 'storePage', regions: [] },
|
|
849
|
+
* },
|
|
850
|
+
* },
|
|
851
|
+
* defaultVariation: 'default-homepage',
|
|
852
|
+
* visibilityRules: {},
|
|
853
|
+
* };
|
|
854
|
+
*
|
|
855
|
+
* // VIP shopper — matches first variation
|
|
856
|
+
* const result = await getPageFromManifest(manifest, {
|
|
857
|
+
* contextResolver: async () => ({
|
|
858
|
+
* customerGroups: { 'vip-customers': true },
|
|
859
|
+
* campaignQualifiers: {},
|
|
860
|
+
* }),
|
|
861
|
+
* });
|
|
862
|
+
* // result.entry === manifest.variations['vip-homepage']
|
|
863
|
+
*
|
|
864
|
+
* // Non-VIP shopper outside holiday window — falls back to default
|
|
865
|
+
* const fallback = await getPageFromManifest(manifest, {
|
|
866
|
+
* contextResolver: async () => ({
|
|
867
|
+
* customerGroups: {},
|
|
868
|
+
* campaignQualifiers: {},
|
|
869
|
+
* }),
|
|
870
|
+
* });
|
|
871
|
+
* // fallback.entry === manifest.variations['default-homepage']
|
|
872
|
+
* ```
|
|
873
|
+
*/
|
|
874
|
+
declare function getPageFromManifest(manifest: PageManifest, {
|
|
875
|
+
contextResolver
|
|
876
|
+
}: {
|
|
877
|
+
contextResolver?: () => Promise<QualifierContext>;
|
|
878
|
+
}): Promise<{
|
|
879
|
+
entry: VariationEntry;
|
|
880
|
+
context: QualifierContext | null;
|
|
881
|
+
} | null>;
|
|
882
|
+
//#endregion
|
|
883
|
+
//#region src/design/data/manifest/content-assignment-resolvers.d.ts
|
|
884
|
+
/**
|
|
885
|
+
* The result of resolving an identifier through a content assignment resolver.
|
|
886
|
+
* Contains the object type, aspect type, and ordered list of keys to search
|
|
887
|
+
* in the site manifest's content assignments.
|
|
888
|
+
*/
|
|
889
|
+
interface ResolvedContentAssignmentLookup {
|
|
890
|
+
/** The type of commerce object (e.g. `'product'`, `'category'`). */
|
|
891
|
+
objectType: string;
|
|
892
|
+
/** Ordered list of object IDs to search in the site manifest's content assignments. */
|
|
893
|
+
keys: string[];
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* A function that converts an identifier key (e.g., a product or category ID)
|
|
897
|
+
* into a {@link ResolvedContentAssignmentLookup} describing where to search
|
|
898
|
+
* in the site manifest for the assigned page ID.
|
|
899
|
+
*/
|
|
900
|
+
type ContentAssignmentResolver = (key: string, manifest?: SiteManifest) => ResolvedContentAssignmentLookup;
|
|
901
|
+
/**
|
|
902
|
+
* Registry of content assignment resolvers keyed by {@link IdentifierType}.
|
|
903
|
+
* Each resolver knows how to convert its identifier type into a set of lookup
|
|
904
|
+
* keys for the site manifest.
|
|
905
|
+
*
|
|
906
|
+
* Built-in resolvers:
|
|
907
|
+
* - **`'product'`** — Maps a product ID to a single PDP lookup key.
|
|
908
|
+
* - **`'category'`** — Maps a category ID to an ordered list of keys that
|
|
909
|
+
* traverses the category hierarchy from child to root, enabling inherited
|
|
910
|
+
* page assignments.
|
|
911
|
+
*
|
|
912
|
+
* The `'page'` identifier type has no resolver — page IDs are used directly.
|
|
913
|
+
*
|
|
914
|
+
* @example
|
|
915
|
+
* ```ts
|
|
916
|
+
* import { ContentAssignmentResolvers } from '@salesforce/storefront-next-runtime/design/data';
|
|
917
|
+
*
|
|
918
|
+
* // Resolve a product identifier for PDP lookup
|
|
919
|
+
* const productResolver = ContentAssignmentResolvers.get('product');
|
|
920
|
+
* productResolver('nike-air-max-90');
|
|
921
|
+
* // => { objectType: 'product', aspectType: 'pdp', keys: ['nike-air-max-90'] }
|
|
922
|
+
*
|
|
923
|
+
* // Resolve a category identifier — traverses hierarchy to find inherited assignments
|
|
924
|
+
* const categoryResolver = ContentAssignmentResolvers.get('category');
|
|
925
|
+
* const siteManifest = {
|
|
926
|
+
* categories: {
|
|
927
|
+
* 'mens-running-shoes': { name: 'Running Shoes', parentCategory: 'mens-shoes' },
|
|
928
|
+
* 'mens-shoes': { name: "Men's Shoes", parentCategory: 'mens' },
|
|
929
|
+
* 'mens': { name: 'Men' },
|
|
930
|
+
* },
|
|
931
|
+
* contentObjectAssignments: {},
|
|
932
|
+
* };
|
|
933
|
+
* categoryResolver('mens-running-shoes', siteManifest);
|
|
934
|
+
* // => { objectType: 'category', aspectType: 'plp', keys: ['mens-running-shoes', 'mens-shoes', 'mens'] }
|
|
935
|
+
* ```
|
|
936
|
+
*/
|
|
937
|
+
declare const ContentAssignmentResolvers: Map<string, ContentAssignmentResolver>;
|
|
938
|
+
//#endregion
|
|
939
|
+
//#region src/design/data/validate-rule.d.ts
|
|
940
|
+
/**
|
|
941
|
+
* Evaluates a visibility rule against a shopper's qualifier context.
|
|
942
|
+
*
|
|
943
|
+
* Campaign-based and non-campaign rules are **mutually exclusive** paths,
|
|
944
|
+
* matching the server's `VisibilityDefinition.isVisible()` logic:
|
|
945
|
+
*
|
|
946
|
+
* - **Campaign-based rule** (has `campaignQualifiers`): only the campaign
|
|
947
|
+
* qualifiers are checked. Schedule and customer-group fields are ignored
|
|
948
|
+
* because the campaign qualification already incorporates those checks
|
|
949
|
+
* server-side.
|
|
950
|
+
* - **Non-campaign rule**: schedule AND customer groups are checked. All
|
|
951
|
+
* specified conditions must pass.
|
|
952
|
+
*
|
|
953
|
+
* When no context is provided and the rule requires campaign or customer group
|
|
954
|
+
* checks, those checks will fail (returning `false`). Schedule checks do not
|
|
955
|
+
* require context and are evaluated against `Date.now()`.
|
|
956
|
+
*
|
|
957
|
+
* @param rule - The visibility rule to evaluate.
|
|
958
|
+
* @param context - The shopper's active qualifiers, or `null`/`undefined` if not yet resolved.
|
|
959
|
+
* @returns `true` if the rule's conditions pass, `false` otherwise.
|
|
960
|
+
*
|
|
961
|
+
* @example
|
|
962
|
+
* ```ts
|
|
963
|
+
* import { validateRule } from '@salesforce/storefront-next-runtime/design/data';
|
|
964
|
+
*
|
|
965
|
+
* // Campaign-based rule — only campaign qualifiers are evaluated
|
|
966
|
+
* const campaignRule = {
|
|
967
|
+
* campaignQualifiers: [{ campaignId: 'holiday-sale-2026', promotionId: 'free-shipping' }],
|
|
968
|
+
* };
|
|
969
|
+
*
|
|
970
|
+
* // Non-campaign rule — schedule AND customer groups are evaluated
|
|
971
|
+
* const segmentRule = {
|
|
972
|
+
* customerGroups: ['vip-customers'],
|
|
973
|
+
* schedule: {
|
|
974
|
+
* start: new Date('2026-12-01').toISOString(),
|
|
975
|
+
* end: new Date('2026-12-31').toISOString(),
|
|
976
|
+
* },
|
|
977
|
+
* };
|
|
978
|
+
* ```
|
|
979
|
+
*/
|
|
980
|
+
declare function validateRule(rule: VisibilityRuleDef, context?: QualifierContext | null): boolean;
|
|
981
|
+
//#endregion
|
|
982
|
+
export { CampaignQualifier, type ComponentDataBinding, ContentAssignmentResolvers, type DataBindingContext, DataBindingRequirement, IdentifierType, InferNodeFromType, ManifestStorage, PageManifest, PageManifestContext, type PageProcessorContext, type PageVisitor, QualifierContext, RequiredError, ResolvedDataBinding, SiteManifest, VariationEntry, VisibilityRuleDef, type VisitorContext, VisitorContextType, getPageFromManifest, parseExpression, processPage, resolveComponentDataBindings, resolveDynamicPageId, resolveExpression, resolvePage, transformComponent, transformPage, transformRegion, validateRule };
|
|
983
|
+
//# sourceMappingURL=design-data.d.ts.map
|