@salesforce/storefront-next-runtime 0.4.2 → 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.
- package/README.md +9 -3
- package/dist/ComponentContext.js +199 -4
- package/dist/ComponentContext.js.map +1 -1
- package/dist/DesignComponent.js +2 -2
- package/dist/DesignRegion.js +2 -2
- package/dist/RegionContext.js +9 -0
- package/dist/RegionContext.js.map +1 -0
- package/dist/component.types.d.ts +1 -1
- package/dist/config.d.ts +34 -221
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +35 -116
- package/dist/config.js.map +1 -1
- package/dist/data-store.d.ts +185 -15
- package/dist/data-store.d.ts.map +1 -1
- package/dist/data-store.js +412 -10
- package/dist/data-store.js.map +1 -1
- package/dist/defaults.d.ts +106 -0
- package/dist/defaults.d.ts.map +1 -0
- package/dist/defaults.js +67 -0
- package/dist/defaults.js.map +1 -0
- package/dist/design-data.d.ts +238 -356
- package/dist/design-data.d.ts.map +1 -1
- package/dist/design-data.js +459 -30
- package/dist/design-data.js.map +1 -1
- package/dist/design-mode.d.ts +3 -2
- package/dist/design-mode.d.ts.map +1 -1
- package/dist/design-react-core.d.ts +5 -15
- package/dist/design-react-core.d.ts.map +1 -1
- package/dist/design-react-core.js +2 -2
- package/dist/design-react.d.ts +2 -2
- package/dist/design.d.ts +2 -2
- package/dist/events.d.ts +32 -6
- package/dist/events.d.ts.map +1 -1
- package/dist/i18n-client.d.ts.map +1 -1
- package/dist/i18n-client.js.map +1 -1
- package/dist/i18n.d.ts +1 -2
- package/dist/i18n.d.ts.map +1 -1
- package/dist/modeDetection.js +0 -18
- package/dist/modeDetection.js.map +1 -1
- package/dist/scapi.d.ts +2185 -466
- package/dist/scapi.d.ts.map +1 -1
- package/dist/scapi.js +1 -1
- package/dist/scapi.js.map +1 -1
- package/dist/schema.d.ts +17 -15
- package/dist/schema.d.ts.map +1 -1
- package/dist/security-react.d.ts +34 -0
- package/dist/security-react.d.ts.map +1 -0
- package/dist/security-react.js +21 -0
- package/dist/security-react.js.map +1 -0
- package/dist/security.d.ts +61 -0
- package/dist/security.d.ts.map +1 -0
- package/dist/security.js +304 -0
- package/dist/security.js.map +1 -0
- package/dist/site-context.d.ts +43 -27
- package/dist/site-context.d.ts.map +1 -1
- package/dist/site-context.js +2 -2
- package/dist/site-context2.js +41 -31
- package/dist/site-context2.js.map +1 -1
- package/dist/types.d.ts +19 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types2.d.ts +89 -63
- package/dist/types2.d.ts.map +1 -1
- package/dist/types3.d.ts +1 -35
- package/dist/types3.d.ts.map +1 -1
- package/package.json +15 -20
- package/dist/DesignFrame.js +0 -204
- package/dist/DesignFrame.js.map +0 -1
- package/dist/custom-global-preferences.d.ts +0 -20
- package/dist/custom-global-preferences.d.ts.map +0 -1
- package/dist/custom-global-preferences.js +0 -31
- package/dist/custom-global-preferences.js.map +0 -1
- package/dist/custom-site-preferences.d.ts +0 -20
- package/dist/custom-site-preferences.d.ts.map +0 -1
- package/dist/custom-site-preferences.js +0 -31
- package/dist/custom-site-preferences.js.map +0 -1
- package/dist/data-store-custom-global-preferences.d.ts +0 -2
- package/dist/data-store-custom-global-preferences.js +0 -6
- package/dist/data-store-custom-site-preferences.d.ts +0 -2
- package/dist/data-store-custom-site-preferences.js +0 -6
- package/dist/data-store-gcp-preferences.d.ts +0 -2
- package/dist/data-store-gcp-preferences.js +0 -6
- package/dist/gcp-preferences.d.ts +0 -52
- package/dist/gcp-preferences.d.ts.map +0 -1
- package/dist/gcp-preferences.js +0 -64
- package/dist/gcp-preferences.js.map +0 -1
- package/dist/utils.js +0 -90
- package/dist/utils.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"design-data.d.ts","names":[],"sources":["../src/design/data/
|
|
1
|
+
{"version":3,"file":"design-data.d.ts","names":[],"sources":["../src/design/data/page/attribute-resolution.ts","../src/design/data/types.ts","../src/design/data/page/process-page.ts","../src/design/data/page/transform.ts","../src/design/data/errors/required.ts","../src/design/data/page/resolve-page.ts","../src/design/data/validate-rule.ts"],"sourcesContent":[],"mappings":"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"}
|
package/dist/design-data.js
CHANGED
|
@@ -349,6 +349,24 @@ function transformRegion(region, visitor) {
|
|
|
349
349
|
*/
|
|
350
350
|
const BARE_EXPRESSION_PATTERN = /^(\w+)\.(\w+)$/;
|
|
351
351
|
/**
|
|
352
|
+
* Coerces a string value returned by the data binding API into a boolean or
|
|
353
|
+
* number when the contents represent one. The data provider returns every
|
|
354
|
+
* field as a string, so callers expecting typed values would otherwise receive
|
|
355
|
+
* `"true"` instead of `true` or `"2026"` instead of `2026`.
|
|
356
|
+
*
|
|
357
|
+
* Non-string inputs are returned as-is. Strings that are neither booleans nor
|
|
358
|
+
* finite numbers are returned unchanged.
|
|
359
|
+
*/
|
|
360
|
+
function parseFieldValue(value) {
|
|
361
|
+
if (typeof value !== "string") return value;
|
|
362
|
+
if (value === "true") return true;
|
|
363
|
+
if (value === "false") return false;
|
|
364
|
+
if (value.trim() === "") return value;
|
|
365
|
+
const num = Number(value);
|
|
366
|
+
if (Number.isFinite(num)) return num;
|
|
367
|
+
return value;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
352
370
|
* Parses a binding expression string into its provider type and field name.
|
|
353
371
|
* Supports the bare `type.field` format.
|
|
354
372
|
*
|
|
@@ -389,7 +407,7 @@ function resolveExpression(expression, contexts, dataBindings) {
|
|
|
389
407
|
if (!context) return "";
|
|
390
408
|
const record = dataBindings[context.type]?.[context.id];
|
|
391
409
|
if (!record) return "";
|
|
392
|
-
return record[parsed.field] ?? "";
|
|
410
|
+
return parseFieldValue(record[parsed.field] ?? "");
|
|
393
411
|
}
|
|
394
412
|
/**
|
|
395
413
|
* Resolves data binding expressions for a single component. Replaces attribute
|
|
@@ -452,6 +470,286 @@ function resolveComponentDataBindings(component, binding, dataBindings) {
|
|
|
452
470
|
};
|
|
453
471
|
}
|
|
454
472
|
|
|
473
|
+
//#endregion
|
|
474
|
+
//#region src/design/data/page/markup-url-rewriter.ts
|
|
475
|
+
const STATICLINK_PATTERN = /\?\$staticlink\$/gi;
|
|
476
|
+
const STATICLINK_DELIMITERS_SINGLE = "\":='(>";
|
|
477
|
+
const STATICLINK_DELIMITERS_DOUBLE = [
|
|
478
|
+
"\"[",
|
|
479
|
+
"=[",
|
|
480
|
+
",[",
|
|
481
|
+
" [",
|
|
482
|
+
" ,",
|
|
483
|
+
", "
|
|
484
|
+
];
|
|
485
|
+
let warnedStaticlink = false;
|
|
486
|
+
function rewriteImages(source, ctx) {
|
|
487
|
+
const domain = ctx.pageLibraryDomain;
|
|
488
|
+
if (!domain) {
|
|
489
|
+
if (!warnedStaticlink) {
|
|
490
|
+
warnedStaticlink = true;
|
|
491
|
+
ctx.onWarn?.({
|
|
492
|
+
kind: "staticlink-rewrite-skipped",
|
|
493
|
+
message: "?$staticlink$ rewrite skipped: ctx.pageLibraryDomain is not set",
|
|
494
|
+
typeId: "",
|
|
495
|
+
attrId: "",
|
|
496
|
+
attrType: "markup"
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
return source;
|
|
500
|
+
}
|
|
501
|
+
const resolveStaticUrl = ctx.staticLinkFor ?? ctx.resolveMediaUrl;
|
|
502
|
+
let result = "";
|
|
503
|
+
let lastPos = -1;
|
|
504
|
+
STATICLINK_PATTERN.lastIndex = 0;
|
|
505
|
+
let match = STATICLINK_PATTERN.exec(source);
|
|
506
|
+
if (!match) return source;
|
|
507
|
+
while (match) {
|
|
508
|
+
const pos = match.index;
|
|
509
|
+
const newPos = STATICLINK_PATTERN.lastIndex;
|
|
510
|
+
let startPos = pos - 1;
|
|
511
|
+
while (true) {
|
|
512
|
+
if (startPos <= lastPos) break;
|
|
513
|
+
const ch = source.charAt(startPos);
|
|
514
|
+
if (STATICLINK_DELIMITERS_SINGLE.indexOf(ch) !== -1) {
|
|
515
|
+
if (!(ch === "=" && startPos + 1 < source.length && source.charAt(startPos + 1) === ".")) break;
|
|
516
|
+
}
|
|
517
|
+
if (startPos > 0) {
|
|
518
|
+
const doubleChar = source.substring(startPos - 1, startPos + 1);
|
|
519
|
+
if (STATICLINK_DELIMITERS_DOUBLE.includes(doubleChar)) break;
|
|
520
|
+
}
|
|
521
|
+
startPos--;
|
|
522
|
+
}
|
|
523
|
+
const leftStart = lastPos === -1 ? 0 : lastPos;
|
|
524
|
+
result += source.substring(leftStart, startPos + 1);
|
|
525
|
+
const path = source.substring(startPos + 1, pos);
|
|
526
|
+
if (path.trim().length !== 0) {
|
|
527
|
+
let url = resolveStaticUrl({
|
|
528
|
+
libraryDomain: domain,
|
|
529
|
+
path: path.trim(),
|
|
530
|
+
locale: ctx.locale
|
|
531
|
+
});
|
|
532
|
+
if (path.startsWith(" ")) url = ` ${url}`;
|
|
533
|
+
if (path.endsWith(" ")) url += " ";
|
|
534
|
+
result += url;
|
|
535
|
+
}
|
|
536
|
+
lastPos = newPos;
|
|
537
|
+
match = STATICLINK_PATTERN.exec(source);
|
|
538
|
+
}
|
|
539
|
+
const tailStart = lastPos === -1 ? 0 : lastPos;
|
|
540
|
+
result += source.substring(tailStart);
|
|
541
|
+
return result;
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Rewrites `?$staticlink$` placeholders in markup to fully-qualified
|
|
545
|
+
* static-content URLs. Pipeline-action placeholders pass through unchanged.
|
|
546
|
+
*/
|
|
547
|
+
function rewriteMarkup(source, ctx) {
|
|
548
|
+
if (!source) return "";
|
|
549
|
+
return rewriteImages(source, ctx);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
//#endregion
|
|
553
|
+
//#region src/design/data/page/attribute-resolution.ts
|
|
554
|
+
/**
|
|
555
|
+
* Module-scoped dedup set for unknown-type / malformed-envelope warnings.
|
|
556
|
+
* Keyed by `${kind}|${typeId}|${attrId}|${attrType}` so two different
|
|
557
|
+
* issues on the same attribute (e.g. malformed-image then later
|
|
558
|
+
* unknown-type) both fire once.
|
|
559
|
+
*/
|
|
560
|
+
const warnedKeys = /* @__PURE__ */ new Set();
|
|
561
|
+
/**
|
|
562
|
+
* Routes a structured warning to the consumer's `onWarn` handler at most
|
|
563
|
+
* once per `(kind, typeId, attrId, attrType)` triple. When no handler is
|
|
564
|
+
* configured the runtime stays silent — production callers are expected to
|
|
565
|
+
* supply a handler.
|
|
566
|
+
*/
|
|
567
|
+
function warnOnce(ctx, kind, typeId, attrId, attrType, message) {
|
|
568
|
+
if (!ctx.onWarn) return;
|
|
569
|
+
const key = `${kind}|${typeId}|${attrId}|${attrType}`;
|
|
570
|
+
if (warnedKeys.has(key)) return;
|
|
571
|
+
warnedKeys.add(key);
|
|
572
|
+
ctx.onWarn({
|
|
573
|
+
kind,
|
|
574
|
+
message,
|
|
575
|
+
typeId,
|
|
576
|
+
attrId,
|
|
577
|
+
attrType
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Returns true when `value` is shaped like an {@link ImageEnvelope}. Used
|
|
582
|
+
* during structural dispatch (when `componentTypes` is unavailable) to
|
|
583
|
+
* recognize image attributes without `attrDef.type`.
|
|
584
|
+
*/
|
|
585
|
+
function isImageEnvelope(value) {
|
|
586
|
+
if (!value || typeof value !== "object") return false;
|
|
587
|
+
const media = value.media;
|
|
588
|
+
return media != null && typeof media === "object" && typeof media.libraryDomain === "string" && typeof media.path === "string";
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Converts an {@link ImageEnvelope} to the resolved SCAPI shape by stamping
|
|
592
|
+
* the URL. Returns the original value untouched if the envelope is
|
|
593
|
+
* malformed (missing `media.libraryDomain` or `media.path`); a warning is
|
|
594
|
+
* logged once per `(typeId, attrId, attrType)` triple so production logs
|
|
595
|
+
* don't drown.
|
|
596
|
+
*/
|
|
597
|
+
function resolveImageAttribute(value, typeId, attrId, attrType, ctx) {
|
|
598
|
+
if (!isImageEnvelope(value)) {
|
|
599
|
+
warnOnce(ctx, "malformed-image", typeId, attrId, attrType, "malformed image envelope, passing through unchanged");
|
|
600
|
+
return value;
|
|
601
|
+
}
|
|
602
|
+
const out = { url: ctx.resolveMediaUrl({
|
|
603
|
+
libraryDomain: value.media.libraryDomain,
|
|
604
|
+
path: value.media.path,
|
|
605
|
+
locale: ctx.locale
|
|
606
|
+
}) };
|
|
607
|
+
if (value.focalPoint) out.focalPoint = value.focalPoint;
|
|
608
|
+
if (value.metaData) out.metaData = value.metaData;
|
|
609
|
+
return out;
|
|
610
|
+
}
|
|
611
|
+
function isFileEnvelope(value) {
|
|
612
|
+
if (!value || typeof value !== "object") return false;
|
|
613
|
+
const candidate = value;
|
|
614
|
+
const media = candidate.media;
|
|
615
|
+
return media != null && typeof media === "object" && typeof media.libraryDomain === "string" && typeof media.path === "string" && !("focalPoint" in candidate || "metaData" in candidate);
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* Resolves a file envelope to a URL string. Matches SCAPI's
|
|
619
|
+
* `mediaFile.getAbsURL().toString()` — file attributes emit a plain URL
|
|
620
|
+
* string, not an object envelope.
|
|
621
|
+
*/
|
|
622
|
+
function resolveFileAttribute(value, typeId, attrId, ctx) {
|
|
623
|
+
if (!isFileEnvelope(value)) {
|
|
624
|
+
warnOnce(ctx, "malformed-file", typeId, attrId, "file", "malformed file envelope, passing through unchanged");
|
|
625
|
+
return value;
|
|
626
|
+
}
|
|
627
|
+
return ctx.resolveMediaUrl({
|
|
628
|
+
libraryDomain: value.media.libraryDomain,
|
|
629
|
+
path: value.media.path,
|
|
630
|
+
locale: ctx.locale
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
const MAX_CMS_RECORD_DEPTH = 10;
|
|
634
|
+
function isCmsRecordEnvelope(value) {
|
|
635
|
+
if (!value || typeof value !== "object") return false;
|
|
636
|
+
const candidate = value;
|
|
637
|
+
if (typeof candidate.id !== "string") return false;
|
|
638
|
+
const type = candidate.type;
|
|
639
|
+
if (!type || typeof type !== "object" || typeof type.id !== "string") return false;
|
|
640
|
+
if (!Array.isArray(type.attributeDefinitions)) return false;
|
|
641
|
+
return candidate.attributes != null && typeof candidate.attributes === "object";
|
|
642
|
+
}
|
|
643
|
+
function resolveCmsRecordAttribute(value, typeId, attrId, ctx, depth) {
|
|
644
|
+
if (value == null) return value;
|
|
645
|
+
if (!isCmsRecordEnvelope(value)) {
|
|
646
|
+
warnOnce(ctx, "malformed-cms-record", typeId, attrId, "cms_record", "malformed cms_record envelope, passing through unchanged");
|
|
647
|
+
return value;
|
|
648
|
+
}
|
|
649
|
+
if (depth >= MAX_CMS_RECORD_DEPTH) {
|
|
650
|
+
warnOnce(ctx, "cms-record-depth-exceeded", typeId, attrId, "cms_record", `cms_record nesting depth exceeded (max ${MAX_CMS_RECORD_DEPTH}), passing through unchanged`);
|
|
651
|
+
return value;
|
|
652
|
+
}
|
|
653
|
+
const innerDefs = value.type.attributeDefinitions;
|
|
654
|
+
const resolvedAttrs = resolveCmsRecordInnerAttributes(value.attributes, typeId, innerDefs, ctx, depth + 1);
|
|
655
|
+
return {
|
|
656
|
+
id: value.id,
|
|
657
|
+
type: value.type,
|
|
658
|
+
attributes: resolvedAttrs
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
function resolveCmsRecordInnerAttributes(data, typeId, defs, ctx, depth) {
|
|
662
|
+
const out = {};
|
|
663
|
+
const defsById = /* @__PURE__ */ new Map();
|
|
664
|
+
for (const def of defs) defsById.set(def.id, def);
|
|
665
|
+
for (const [attrId, value] of Object.entries(data)) {
|
|
666
|
+
const def = defsById.get(attrId);
|
|
667
|
+
if (!def) {
|
|
668
|
+
out[attrId] = value;
|
|
669
|
+
continue;
|
|
670
|
+
}
|
|
671
|
+
out[attrId] = dispatchCmsRecordInner(value, typeId, attrId, def, ctx, depth);
|
|
672
|
+
}
|
|
673
|
+
return out;
|
|
674
|
+
}
|
|
675
|
+
function dispatchCmsRecordInner(value, typeId, attrId, attrDef, ctx, depth) {
|
|
676
|
+
if (attrDef.type === "cms_record") return resolveCmsRecordAttribute(value, typeId, attrId, ctx, depth);
|
|
677
|
+
return dispatchByType(value, typeId, attrId, attrDef, ctx);
|
|
678
|
+
}
|
|
679
|
+
/**
|
|
680
|
+
* Resolves every attribute on a component's `data` map to the wire shape
|
|
681
|
+
* SCAPI `getPage` would have returned.
|
|
682
|
+
*
|
|
683
|
+
* Dispatch is type-driven when {@code typeAttributeDefinitions} is supplied.
|
|
684
|
+
* Otherwise the resolver inspects each value structurally — it recognizes
|
|
685
|
+
* the image envelope by the presence of `media.libraryDomain` and
|
|
686
|
+
* `media.path` and passes everything else through unchanged.
|
|
687
|
+
*
|
|
688
|
+
* Forward-compatibility (Q9): unknown attribute types pass through. Each
|
|
689
|
+
* `(typeId, attrId, attrType)` triple is logged once per process via a
|
|
690
|
+
* module-scoped dedup set.
|
|
691
|
+
*
|
|
692
|
+
* @param data attribute map to resolve, already
|
|
693
|
+
* locale-merged + data-binding-resolved by
|
|
694
|
+
* {@link processPage}.
|
|
695
|
+
* @param typeId component type identifier, used as part
|
|
696
|
+
* of the dedup key for warnings. Empty
|
|
697
|
+
* string is acceptable for anonymous
|
|
698
|
+
* callers (page-level data).
|
|
699
|
+
* @param typeAttributeDefinitions attribute definitions for {@code typeId}
|
|
700
|
+
* from `manifest.componentTypes`. When
|
|
701
|
+
* omitted, falls back to structural
|
|
702
|
+
* detection of the image envelope.
|
|
703
|
+
* @param ctx per-request resolution surface.
|
|
704
|
+
* @returns a new map with each attribute's value replaced by the resolved
|
|
705
|
+
* wire shape; pass-through for any attribute type the resolver
|
|
706
|
+
* doesn't yet recognize.
|
|
707
|
+
*/
|
|
708
|
+
function resolveAttributeValues(data, typeId, typeAttributeDefinitions, ctx) {
|
|
709
|
+
if (!data) return {};
|
|
710
|
+
const out = {};
|
|
711
|
+
if (typeAttributeDefinitions && Object.keys(typeAttributeDefinitions).length > 0) {
|
|
712
|
+
for (const [attrId, value] of Object.entries(data)) {
|
|
713
|
+
const def = typeAttributeDefinitions[attrId];
|
|
714
|
+
if (!def) {
|
|
715
|
+
out[attrId] = value;
|
|
716
|
+
continue;
|
|
717
|
+
}
|
|
718
|
+
out[attrId] = dispatchByType(value, typeId, attrId, def, ctx);
|
|
719
|
+
}
|
|
720
|
+
return out;
|
|
721
|
+
}
|
|
722
|
+
for (const [attrId, value] of Object.entries(data)) if (isImageEnvelope(value)) out[attrId] = resolveImageAttribute(value, typeId, attrId, "image", ctx);
|
|
723
|
+
else out[attrId] = value;
|
|
724
|
+
return out;
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Type-driven dispatch. Unknown types fall through with a deduped warning
|
|
728
|
+
* (Q9) — the principle is that a runtime older than ECOM should still
|
|
729
|
+
* produce *something* rather than dropping the value.
|
|
730
|
+
*/
|
|
731
|
+
function dispatchByType(value, typeId, attrId, attrDef, ctx) {
|
|
732
|
+
switch (attrDef.type) {
|
|
733
|
+
case "image": return resolveImageAttribute(value, typeId, attrId, attrDef.type, ctx);
|
|
734
|
+
case "markup": return typeof value === "string" ? rewriteMarkup(value, ctx) : value;
|
|
735
|
+
case "file": return resolveFileAttribute(value, typeId, attrId, ctx);
|
|
736
|
+
case "cms_record": return resolveCmsRecordAttribute(value, typeId, attrId, ctx, 0);
|
|
737
|
+
case "string":
|
|
738
|
+
case "text":
|
|
739
|
+
case "url":
|
|
740
|
+
case "boolean":
|
|
741
|
+
case "integer":
|
|
742
|
+
case "enum":
|
|
743
|
+
case "custom":
|
|
744
|
+
case "product":
|
|
745
|
+
case "category":
|
|
746
|
+
case "page": return value;
|
|
747
|
+
default:
|
|
748
|
+
warnOnce(ctx, "unknown-attribute-type", typeId, attrId, attrDef.type, "unknown attribute type, passing through unchanged");
|
|
749
|
+
return value;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
|
|
455
753
|
//#endregion
|
|
456
754
|
//#region src/design/data/validate-rule.ts
|
|
457
755
|
/**
|
|
@@ -498,7 +796,7 @@ function resolveComponentDataBindings(component, binding, dataBindings) {
|
|
|
498
796
|
* ```
|
|
499
797
|
*/
|
|
500
798
|
function validateRule(rule, locale, context) {
|
|
501
|
-
if (rule.campaignQualifiers) {
|
|
799
|
+
if (rule.campaignQualifiers?.length) {
|
|
502
800
|
for (const campaignQualifier of rule.campaignQualifiers) if (!context?.campaignQualifiers?.[campaignQualifier.campaignId]?.[campaignQualifier.promotionId]) return false;
|
|
503
801
|
} else {
|
|
504
802
|
if (rule.activeLocales && !rule.activeLocales.includes(locale)) return false;
|
|
@@ -573,9 +871,57 @@ function validateRule(rule, locale, context) {
|
|
|
573
871
|
* // "loyalty-offer" was removed because the shopper isn't a loyalty member
|
|
574
872
|
* ```
|
|
575
873
|
*/
|
|
874
|
+
/**
|
|
875
|
+
* Builds a component's `data` map by walking each attribute definition and
|
|
876
|
+
* picking the first non-undefined value in priority order:
|
|
877
|
+
*
|
|
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).
|
|
886
|
+
*
|
|
887
|
+
* If none of those have a value the attribute is omitted from the result.
|
|
888
|
+
*
|
|
889
|
+
* When no `typeDefs` are supplied, we fall back to the legacy behavior:
|
|
890
|
+
* `{ ...nodeData, ...fallbackContent, ...localeContent }`. This keeps
|
|
891
|
+
* already-deployed manifests rendering until the manifest builder starts
|
|
892
|
+
* emitting `componentTypes`.
|
|
893
|
+
*/
|
|
894
|
+
function composeComponentData({ nodeData, literalDefaultContent, defaultContent, localeContent, typeDefs }) {
|
|
895
|
+
const fallbackContent = Object.keys(defaultContent).length > 0 ? defaultContent : literalDefaultContent;
|
|
896
|
+
if (!typeDefs || Object.keys(typeDefs).length === 0) return {
|
|
897
|
+
...nodeData ?? {},
|
|
898
|
+
...fallbackContent,
|
|
899
|
+
...localeContent
|
|
900
|
+
};
|
|
901
|
+
const result = {};
|
|
902
|
+
for (const attrId of Object.keys(typeDefs)) {
|
|
903
|
+
const def = typeDefs[attrId];
|
|
904
|
+
if (Object.prototype.hasOwnProperty.call(localeContent, attrId)) result[attrId] = localeContent[attrId];
|
|
905
|
+
else if (Object.prototype.hasOwnProperty.call(fallbackContent, attrId)) result[attrId] = fallbackContent[attrId];
|
|
906
|
+
else if (def.defaultValue !== void 0) result[attrId] = def.defaultValue;
|
|
907
|
+
}
|
|
908
|
+
return result;
|
|
909
|
+
}
|
|
576
910
|
function processPage(page, processorContext) {
|
|
577
911
|
const { pruneInvisible = true } = processorContext;
|
|
578
912
|
return transformPage(page, {
|
|
913
|
+
visitPage(ctx) {
|
|
914
|
+
const pageNode = ctx.node;
|
|
915
|
+
const result = {
|
|
916
|
+
...pageNode,
|
|
917
|
+
regions: ctx.visitRegions(pageNode.regions)
|
|
918
|
+
};
|
|
919
|
+
if (pageNode.data !== void 0) {
|
|
920
|
+
const typeDefs = processorContext.componentTypes?.[pageNode.typeId]?.attributeDefinitions;
|
|
921
|
+
result.data = resolveAttributeValues(pageNode.data, pageNode.typeId, typeDefs, processorContext.attrCtx);
|
|
922
|
+
}
|
|
923
|
+
return result;
|
|
924
|
+
},
|
|
579
925
|
visitRegion(ctx) {
|
|
580
926
|
let regionInfo;
|
|
581
927
|
if (ctx.parent?.type === "page") regionInfo = processorContext.pageInfo.regions[ctx.node.id];
|
|
@@ -610,23 +956,34 @@ function processPage(page, processorContext) {
|
|
|
610
956
|
isVisible = false;
|
|
611
957
|
}
|
|
612
958
|
}
|
|
613
|
-
const
|
|
959
|
+
const literalDefaultContent = componentInfo?.content?.default ?? {};
|
|
960
|
+
const defaultContent = componentInfo?.content?.[processorContext.defaultLocale] ?? {};
|
|
614
961
|
const localeContent = componentInfo?.content?.[processorContext.locale] ?? {};
|
|
615
|
-
const content = {
|
|
616
|
-
...defaultContent,
|
|
617
|
-
...localeContent
|
|
618
|
-
};
|
|
619
962
|
const isLocalized = Boolean(componentInfo?.content?.[processorContext.locale]);
|
|
963
|
+
const typeDefs = processorContext.componentTypes?.[ctx.node.typeId]?.attributeDefinitions;
|
|
964
|
+
const composedData = composeComponentData({
|
|
965
|
+
nodeData: ctx.node.data,
|
|
966
|
+
literalDefaultContent,
|
|
967
|
+
defaultContent,
|
|
968
|
+
localeContent,
|
|
969
|
+
typeDefs
|
|
970
|
+
});
|
|
971
|
+
const name = componentInfo?.name ?? ctx.node.name;
|
|
972
|
+
const fragment = componentInfo?.fragment ?? ctx.node.fragment ?? false;
|
|
620
973
|
let node = {
|
|
621
974
|
...ctx.node,
|
|
975
|
+
name,
|
|
976
|
+
fragment,
|
|
622
977
|
localized: isLocalized,
|
|
623
978
|
visible: isVisible,
|
|
624
|
-
data:
|
|
625
|
-
...ctx.node.data,
|
|
626
|
-
...content
|
|
627
|
-
}
|
|
979
|
+
data: composedData
|
|
628
980
|
};
|
|
629
981
|
node = resolveComponentDataBindings(node, componentInfo?.dataBinding, processorContext.qualifiers?.dataBindings);
|
|
982
|
+
const resolvedData = resolveAttributeValues(node.data, node.typeId, typeDefs, processorContext.attrCtx);
|
|
983
|
+
node = {
|
|
984
|
+
...node,
|
|
985
|
+
data: resolvedData
|
|
986
|
+
};
|
|
630
987
|
return {
|
|
631
988
|
...node,
|
|
632
989
|
regions: ctx.visitRegions(ctx.node.regions)
|
|
@@ -721,18 +1078,43 @@ const ContentAssignmentResolvers = new Map([["product", (key) => ({
|
|
|
721
1078
|
//#endregion
|
|
722
1079
|
//#region src/design/data/manifest/resolve-dynamic-page-id.ts
|
|
723
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
|
+
/**
|
|
724
1097
|
* Converts a product or category identifier into a page ID by looking up
|
|
725
1098
|
* content assignments in the site manifest. For categories, the lookup
|
|
726
1099
|
* traverses the category hierarchy from the given category up to the root,
|
|
727
1100
|
* returning the first matching assignment.
|
|
728
1101
|
*
|
|
729
|
-
*
|
|
730
|
-
*
|
|
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.
|
|
731
1111
|
*
|
|
732
1112
|
* @param options - The resolution options.
|
|
733
1113
|
* @param options.id - The identifier to resolve (product ID, category ID, or page ID).
|
|
734
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'`).
|
|
735
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.
|
|
736
1118
|
* @returns The resolved page ID, or `null` if no assignment was found.
|
|
737
1119
|
*
|
|
738
1120
|
* @example
|
|
@@ -757,25 +1139,31 @@ const ContentAssignmentResolvers = new Map([["product", (key) => ({
|
|
|
757
1139
|
* };
|
|
758
1140
|
*
|
|
759
1141
|
* // Direct match
|
|
760
|
-
* resolveDynamicPageId({ id: 'mens-shoes', identifierType: 'category', siteManifest });
|
|
1142
|
+
* await resolveDynamicPageId({ id: 'mens-shoes', identifierType: 'category', aspectType: 'plp', siteManifest });
|
|
761
1143
|
* // => 'page-mens-shoes-plp'
|
|
762
1144
|
*
|
|
763
1145
|
* // Inherited from parent category
|
|
764
|
-
* resolveDynamicPageId({ id: 'mens-running-shoes', identifierType: 'category', siteManifest });
|
|
1146
|
+
* await resolveDynamicPageId({ id: 'mens-running-shoes', identifierType: 'category', aspectType: 'plp', siteManifest });
|
|
765
1147
|
* // => 'page-mens-shoes-plp' (found via parent traversal)
|
|
766
1148
|
*
|
|
767
|
-
* //
|
|
768
|
-
* resolveDynamicPageId({
|
|
769
|
-
*
|
|
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'
|
|
770
1158
|
* ```
|
|
771
1159
|
*/
|
|
772
|
-
function resolveDynamicPageId({ id, identifierType, siteManifest, aspectType }) {
|
|
773
|
-
const
|
|
774
|
-
if (
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
return
|
|
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);
|
|
779
1167
|
}
|
|
780
1168
|
|
|
781
1169
|
//#endregion
|
|
@@ -878,6 +1266,36 @@ async function getPageFromManifest(manifest, { contextResolver, locale }) {
|
|
|
878
1266
|
//#endregion
|
|
879
1267
|
//#region src/design/data/page/resolve-page.ts
|
|
880
1268
|
/**
|
|
1269
|
+
* Page metadata fields the manifest builder may locale-overlay. Used by
|
|
1270
|
+
* {@link applyPageMetadataOverlay} to know which keys to copy from the
|
|
1271
|
+
* overlay onto the resolved page; structural fields like `id`, `typeId`,
|
|
1272
|
+
* and `regions` are intentionally excluded.
|
|
1273
|
+
*/
|
|
1274
|
+
const PAGE_METADATA_OVERLAY_KEYS = [
|
|
1275
|
+
"name",
|
|
1276
|
+
"aspectTypeId",
|
|
1277
|
+
"description",
|
|
1278
|
+
"pageTitle",
|
|
1279
|
+
"pageDescription",
|
|
1280
|
+
"pageKeywords"
|
|
1281
|
+
];
|
|
1282
|
+
/**
|
|
1283
|
+
* Applies a per-locale page metadata overlay to the variation's default-locale
|
|
1284
|
+
* page. The overlay is a **full replacement** for the listed metadata fields
|
|
1285
|
+
* — when a key is present in the overlay it wins; when absent we fall through
|
|
1286
|
+
* to the default-locale value (Q6 of the design plan).
|
|
1287
|
+
*
|
|
1288
|
+
* Returns a shallow copy of the page with overlaid fields applied. Structural
|
|
1289
|
+
* fields (`id`, `typeId`, `regions`, `data`) are never touched.
|
|
1290
|
+
*/
|
|
1291
|
+
function applyPageMetadataOverlay(variation, locale) {
|
|
1292
|
+
const overlay = variation.pageContent?.[locale];
|
|
1293
|
+
if (!overlay) return variation.page;
|
|
1294
|
+
const out = { ...variation.page };
|
|
1295
|
+
for (const key of PAGE_METADATA_OVERLAY_KEYS) if (overlay[key] !== void 0) out[key] = overlay[key];
|
|
1296
|
+
return out;
|
|
1297
|
+
}
|
|
1298
|
+
/**
|
|
881
1299
|
* Main entry point for the page resolution pipeline. Orchestrates the full flow:
|
|
882
1300
|
*
|
|
883
1301
|
* 1. **Resolve dynamic page ID** — For product/category identifiers, looks up
|
|
@@ -897,6 +1315,7 @@ async function getPageFromManifest(manifest, { contextResolver, locale }) {
|
|
|
897
1315
|
* @param options.manifestStorage - Storage implementation for fetching manifests.
|
|
898
1316
|
* @param options.contextResolver - Optional async function that returns the shopper's qualifier context. Only called if a visibility rule needs it.
|
|
899
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.
|
|
900
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.
|
|
901
1320
|
* @returns The fully resolved and filtered page, or `null`.
|
|
902
1321
|
*
|
|
@@ -934,21 +1353,23 @@ async function getPageFromManifest(manifest, { contextResolver, locale }) {
|
|
|
934
1353
|
* }
|
|
935
1354
|
* ```
|
|
936
1355
|
*/
|
|
937
|
-
async function resolvePage({ id, identifierType, aspectType, locale, manifestStorage, contextResolver, pruneInvisible = true }) {
|
|
1356
|
+
async function resolvePage({ id, identifierType, aspectType, categoryId, locale, defaultLocale, manifestStorage, contextResolver, attrCtx, pruneInvisible = true }) {
|
|
938
1357
|
let resolvedId = null;
|
|
939
1358
|
if (ContentAssignmentResolvers.has(identifierType)) {
|
|
940
1359
|
const siteManifest = await manifestStorage.getSiteManifest();
|
|
941
1360
|
RequiredError.assert(aspectType, `Aspect type is required for identifier type ${identifierType}`, (v) => !v);
|
|
942
|
-
resolvedId = resolveDynamicPageId({
|
|
1361
|
+
resolvedId = await resolveDynamicPageId({
|
|
943
1362
|
id,
|
|
944
1363
|
identifierType,
|
|
945
1364
|
aspectType,
|
|
946
|
-
siteManifest
|
|
1365
|
+
siteManifest,
|
|
1366
|
+
categoryId
|
|
947
1367
|
});
|
|
948
1368
|
} else resolvedId = id;
|
|
949
1369
|
if (!resolvedId) return null;
|
|
950
1370
|
const pageManifest = await manifestStorage.getPageManifest(resolvedId);
|
|
951
1371
|
if (!pageManifest) return null;
|
|
1372
|
+
if (pageManifest.context?.dataBindings?.length > 0) return null;
|
|
952
1373
|
const pageResults = await getPageFromManifest(pageManifest, {
|
|
953
1374
|
contextResolver,
|
|
954
1375
|
locale
|
|
@@ -956,15 +1377,23 @@ async function resolvePage({ id, identifierType, aspectType, locale, manifestSto
|
|
|
956
1377
|
if (!pageResults) return null;
|
|
957
1378
|
let context = null;
|
|
958
1379
|
if (pageResults.entry.pageRequiresContext) context = pageResults.context ?? await contextResolver?.(pageManifest.context) ?? null;
|
|
959
|
-
|
|
1380
|
+
const localizedPage = applyPageMetadataOverlay(pageResults.entry, locale);
|
|
1381
|
+
const resolvedAttrCtx = pageManifest.pageLibraryDomain && !attrCtx.pageLibraryDomain ? {
|
|
1382
|
+
...attrCtx,
|
|
1383
|
+
pageLibraryDomain: pageManifest.pageLibraryDomain
|
|
1384
|
+
} : attrCtx;
|
|
1385
|
+
return processPage(localizedPage, {
|
|
960
1386
|
qualifiers: context,
|
|
961
1387
|
componentInfo: pageManifest.componentInfo,
|
|
962
1388
|
pageInfo: { regions: pageResults.entry.regions },
|
|
963
1389
|
locale,
|
|
1390
|
+
defaultLocale,
|
|
1391
|
+
attrCtx: resolvedAttrCtx,
|
|
1392
|
+
componentTypes: pageManifest.componentTypes,
|
|
964
1393
|
pruneInvisible
|
|
965
1394
|
});
|
|
966
1395
|
}
|
|
967
1396
|
|
|
968
1397
|
//#endregion
|
|
969
|
-
export {
|
|
1398
|
+
export { RequiredError, processPage, resolvePage, transformComponent, transformPage, transformRegion, validateRule };
|
|
970
1399
|
//# sourceMappingURL=design-data.js.map
|