@syntrologie/adapt-product 2.29.0 → 2.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cdn.js +1 -1
- package/dist/{chunk-VY23O32Z.js → chunk-QC5B4ITM.js} +126 -7
- package/dist/chunk-QC5B4ITM.js.map +7 -0
- package/dist/native/nativeSlotWatcher.d.ts +2 -0
- package/dist/native/nativeSlotWatcher.d.ts.map +1 -1
- package/dist/presets-store.d.ts +50 -0
- package/dist/presets-store.d.ts.map +1 -0
- package/dist/runtime.js +1 -1
- package/dist/widgets/SyntroPdpLit.d.ts +68 -0
- package/dist/widgets/SyntroPdpLit.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-VY23O32Z.js.map +0 -7
package/dist/cdn.js
CHANGED
|
@@ -208,6 +208,27 @@ var NotifyZ = z.object({
|
|
|
208
208
|
icon: z.string().optional()
|
|
209
209
|
}).nullable().optional();
|
|
210
210
|
|
|
211
|
+
// src/presets-store.ts
|
|
212
|
+
function presetsWindow() {
|
|
213
|
+
if (typeof window === "undefined") return void 0;
|
|
214
|
+
return window;
|
|
215
|
+
}
|
|
216
|
+
function getPdpPresets() {
|
|
217
|
+
return presetsWindow()?.__SYNTRO_PDP_PRESETS__;
|
|
218
|
+
}
|
|
219
|
+
function pdpPresetsFetchStarted() {
|
|
220
|
+
const pending2 = presetsWindow()?.__SYNTRO_PDP_PRESETS_PROMISE__;
|
|
221
|
+
return pending2 != null && typeof pending2.then === "function";
|
|
222
|
+
}
|
|
223
|
+
function pdpPresetsReady() {
|
|
224
|
+
const pending2 = presetsWindow()?.__SYNTRO_PDP_PRESETS_PROMISE__;
|
|
225
|
+
if (!pending2 || typeof pending2.then !== "function") return Promise.resolve();
|
|
226
|
+
return Promise.resolve(pending2).then(
|
|
227
|
+
() => void 0,
|
|
228
|
+
() => void 0
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
211
232
|
// src/scoring/pool-selection.ts
|
|
212
233
|
var UNKNOWN_DECISION = "unknown";
|
|
213
234
|
var DISPLAY_MAX = {
|
|
@@ -1304,8 +1325,7 @@ function _buildLiveMount(slot, kind, payload) {
|
|
|
1304
1325
|
};
|
|
1305
1326
|
}
|
|
1306
1327
|
function _readPdpPresets() {
|
|
1307
|
-
|
|
1308
|
-
return window.__SYNTRO_PDP_PRESETS__;
|
|
1328
|
+
return getPdpPresets();
|
|
1309
1329
|
}
|
|
1310
1330
|
function _titleCase(slug) {
|
|
1311
1331
|
return slug.split(/[-_\s]+/).map((w) => w ? w[0].toUpperCase() + w.slice(1) : "").join(" ");
|
|
@@ -1788,6 +1808,26 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement5 {
|
|
|
1788
1808
|
this.runtimeRef = null;
|
|
1789
1809
|
/** Canonical decision and render state. */
|
|
1790
1810
|
this._decisionState = createInitialPdpState();
|
|
1811
|
+
/**
|
|
1812
|
+
* WI-6b self-identification state. `_selfIdentify` latches true when the
|
|
1813
|
+
* element connects WITHOUT a product-id attr (config-placement shape) —
|
|
1814
|
+
* from then on the page URL, not the attr, owns identity, and SPA
|
|
1815
|
+
* navigations re-resolve it. `_pageUnresolvable` (reactive) makes render()
|
|
1816
|
+
* produce NOTHING — fail-closed, consistent with the missing-preset
|
|
1817
|
+
* behavior: never a broken box on a non-product page.
|
|
1818
|
+
*/
|
|
1819
|
+
this._selfIdentify = false;
|
|
1820
|
+
this._pageUnresolvable = false;
|
|
1821
|
+
this._navUnsub = null;
|
|
1822
|
+
/**
|
|
1823
|
+
* WI-4 presets readiness. False while a config-driven `pdpPresetsUrl`
|
|
1824
|
+
* fetch may still land; flips true (reactive) once `pdpPresetsReady()`
|
|
1825
|
+
* settles — which is immediate when no fetch is in flight. Once settled,
|
|
1826
|
+
* an element whose product still has no preset row renders NOTHING
|
|
1827
|
+
* (fail-closed) instead of an eternal loading skeleton.
|
|
1828
|
+
*/
|
|
1829
|
+
this._presetsSettled = false;
|
|
1830
|
+
this._presetsReadyWired = false;
|
|
1791
1831
|
this._composeStartedAt = 0;
|
|
1792
1832
|
this._lastArchetype = "";
|
|
1793
1833
|
this._mountDurations = /* @__PURE__ */ new Map();
|
|
@@ -1832,12 +1872,22 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement5 {
|
|
|
1832
1872
|
const reconnecting = this._decisionState.connection === "disconnected";
|
|
1833
1873
|
super.connectedCallback();
|
|
1834
1874
|
ensureSectionLayoutStyles();
|
|
1875
|
+
if (!this.productId) this._selfIdentify = true;
|
|
1835
1876
|
this._refreshNativeSlots();
|
|
1836
1877
|
this._syncDecisionRuntime(reconnecting);
|
|
1837
1878
|
this._syncDecisionPortal();
|
|
1879
|
+
if (!this._presetsReadyWired) {
|
|
1880
|
+
this._presetsReadyWired = true;
|
|
1881
|
+
void pdpPresetsReady().then(() => {
|
|
1882
|
+
this._presetsSettled = true;
|
|
1883
|
+
if (this.isConnected) this._syncDecisionRuntime(false);
|
|
1884
|
+
});
|
|
1885
|
+
}
|
|
1838
1886
|
}
|
|
1839
1887
|
disconnectedCallback() {
|
|
1840
1888
|
super.disconnectedCallback();
|
|
1889
|
+
this._navUnsub?.();
|
|
1890
|
+
this._navUnsub = null;
|
|
1841
1891
|
for (const handle of this._nativeHandles.values()) handle.restore();
|
|
1842
1892
|
this._nativeHandles.clear();
|
|
1843
1893
|
this._dispatchDecision({ type: "disconnected" });
|
|
@@ -1880,7 +1930,70 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement5 {
|
|
|
1880
1930
|
if (typeof document === "undefined") return null;
|
|
1881
1931
|
return document.querySelector("[data-syntro-pdp-why-portal]");
|
|
1882
1932
|
}
|
|
1933
|
+
// ── WI-6b: page-URL self-identification ─────────────────────────────────
|
|
1934
|
+
/**
|
|
1935
|
+
* Resolve identity from the page URL when in self-identify mode. Runs at
|
|
1936
|
+
* every runtime sync (connect + runtimeRef arrival) — idempotent. A miss
|
|
1937
|
+
* marks the page unresolvable: render() then produces NOTHING (fail-closed)
|
|
1938
|
+
* and a warning names the URL. While runtimeRef is still null we stay
|
|
1939
|
+
* quiet — the mountable wires it in moments later and we re-run.
|
|
1940
|
+
*/
|
|
1941
|
+
_resolvePageProduct() {
|
|
1942
|
+
if (!this._selfIdentify || !this.runtimeRef) return;
|
|
1943
|
+
const identity = this.runtimeRef.getPageProductIdentity?.() ?? null;
|
|
1944
|
+
if (identity) {
|
|
1945
|
+
this._pageUnresolvable = false;
|
|
1946
|
+
this.productId = identity.productHandle;
|
|
1947
|
+
if (!this.productName && identity.productTitle) this.productName = identity.productTitle;
|
|
1948
|
+
return;
|
|
1949
|
+
}
|
|
1950
|
+
if (!this._pageUnresolvable) {
|
|
1951
|
+
this._pageUnresolvable = true;
|
|
1952
|
+
this.productId = "";
|
|
1953
|
+
const url = typeof window !== "undefined" ? window.location.href : "";
|
|
1954
|
+
console.warn(
|
|
1955
|
+
`[syntro-pdp] no product-id attribute and the page URL did not resolve to a product \u2014 rendering nothing (fail-closed). url=${url}`
|
|
1956
|
+
);
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
/**
|
|
1960
|
+
* Subscribe to the runtime's NavigationMonitor so SPA/PJAX product-page
|
|
1961
|
+
* changes re-resolve identity and re-fire (WI-6b). Self-identified elements
|
|
1962
|
+
* only — an attr-driven element's identity is pinned by its attr. Skips
|
|
1963
|
+
* gracefully when the runtime lacks `navigation` (bare test mounts).
|
|
1964
|
+
*/
|
|
1965
|
+
_wireNavigation() {
|
|
1966
|
+
if (!this._selfIdentify || this._navUnsub) return;
|
|
1967
|
+
const nav = this.runtimeRef?.navigation;
|
|
1968
|
+
if (!nav || typeof nav.subscribe !== "function") return;
|
|
1969
|
+
this._navUnsub = nav.subscribe((url) => this._onPageNavigated(url));
|
|
1970
|
+
}
|
|
1971
|
+
/** Re-resolve on navigation; on a product change reset the decision machine
|
|
1972
|
+
* through its sanctioned disconnect/reconnect path and re-fire. */
|
|
1973
|
+
_onPageNavigated(navUrl) {
|
|
1974
|
+
if (!this._selfIdentify || !this.isConnected || !this.runtimeRef) return;
|
|
1975
|
+
const identity = this.runtimeRef.getPageProductIdentity?.() ?? null;
|
|
1976
|
+
const next = identity?.productHandle ?? "";
|
|
1977
|
+
if (next === this.productId) return;
|
|
1978
|
+
this._dispatchDecision({ type: "disconnected" });
|
|
1979
|
+
if (identity) {
|
|
1980
|
+
this._pageUnresolvable = false;
|
|
1981
|
+
this.productId = identity.productHandle;
|
|
1982
|
+
if (identity.productTitle) this.productName = identity.productTitle;
|
|
1983
|
+
} else {
|
|
1984
|
+
this.productId = "";
|
|
1985
|
+
if (!this._pageUnresolvable) {
|
|
1986
|
+
this._pageUnresolvable = true;
|
|
1987
|
+
console.warn(
|
|
1988
|
+
`[syntro-pdp] navigation left the product surface \u2014 rendering nothing (fail-closed). url=${navUrl}`
|
|
1989
|
+
);
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
this._syncDecisionRuntime(true);
|
|
1993
|
+
}
|
|
1883
1994
|
_syncDecisionRuntime(reconnecting) {
|
|
1995
|
+
this._resolvePageProduct();
|
|
1996
|
+
this._wireNavigation();
|
|
1884
1997
|
let appActive = false;
|
|
1885
1998
|
try {
|
|
1886
1999
|
appActive = Boolean(this.runtimeRef?.apps?.has?.("adaptive-product"));
|
|
@@ -1926,7 +2039,7 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement5 {
|
|
|
1926
2039
|
_warnDecision(code, props) {
|
|
1927
2040
|
if (code === "missing-config") {
|
|
1928
2041
|
console.warn(
|
|
1929
|
-
`[syntro-pdp] no PDP presets for product "${this.productId}" (or the scored-template primitive is unavailable) \u2014 skipping personalization.
|
|
2042
|
+
`[syntro-pdp] no PDP presets for product "${this.productId}" (or the scored-template primitive is unavailable) \u2014 skipping personalization. Deliver presets via the SDK config \`pdpPresetsUrl\` (or the legacy __SYNTRO_PDP_PRESETS__ boot global) to enable the config-first PDP.`
|
|
1930
2043
|
);
|
|
1931
2044
|
return;
|
|
1932
2045
|
}
|
|
@@ -2430,6 +2543,10 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement5 {
|
|
|
2430
2543
|
`;
|
|
2431
2544
|
}
|
|
2432
2545
|
render() {
|
|
2546
|
+
if (this._pageUnresolvable) return html5``;
|
|
2547
|
+
if (this._presetsSettled && this._decisionState.stream.status === "idle" && this._decisionState.plan.status === "empty" && !_readPdpPresets()?.[this.productId]) {
|
|
2548
|
+
return html5``;
|
|
2549
|
+
}
|
|
2433
2550
|
const skeletonStyles = _SyntroPdpLit._skeletonStyles;
|
|
2434
2551
|
const view = selectPdpView(this._decisionState);
|
|
2435
2552
|
if (view.layout === "skeleton") {
|
|
@@ -2502,7 +2619,9 @@ _SyntroPdpLit.properties = {
|
|
|
2502
2619
|
productName: { type: String, attribute: "product-name" },
|
|
2503
2620
|
productBlurb: { type: String, attribute: "product-blurb" },
|
|
2504
2621
|
runtimeRef: { attribute: false },
|
|
2505
|
-
_decisionState: { state: true }
|
|
2622
|
+
_decisionState: { state: true },
|
|
2623
|
+
_pageUnresolvable: { state: true },
|
|
2624
|
+
_presetsSettled: { state: true }
|
|
2506
2625
|
};
|
|
2507
2626
|
/**
|
|
2508
2627
|
* Self-contained skeleton (shimmer) CSS. Rendered inside the component
|
|
@@ -2598,8 +2717,7 @@ function readSynOS() {
|
|
|
2598
2717
|
return window.SynOS;
|
|
2599
2718
|
}
|
|
2600
2719
|
function readPresets() {
|
|
2601
|
-
|
|
2602
|
-
return window.__SYNTRO_PDP_PRESETS__;
|
|
2720
|
+
return getPdpPresets();
|
|
2603
2721
|
}
|
|
2604
2722
|
function readArchetypeDescriptions() {
|
|
2605
2723
|
if (typeof window === "undefined") return {};
|
|
@@ -2705,6 +2823,7 @@ function summaryFor(pair) {
|
|
|
2705
2823
|
return pair.decision !== UNKNOWN ? `Tailored for: ${pair.archetype} \xB7 ${pair.decision}` : `Tailored for: ${pair.archetype}`;
|
|
2706
2824
|
}
|
|
2707
2825
|
async function computeDecision(productId) {
|
|
2826
|
+
if (pdpPresetsFetchStarted()) await pdpPresetsReady();
|
|
2708
2827
|
const productPresets = readPresets()?.[productId];
|
|
2709
2828
|
const score = readSynOS()?.score;
|
|
2710
2829
|
if (!productPresets || typeof score !== "function") {
|
|
@@ -10960,4 +11079,4 @@ export {
|
|
|
10960
11079
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
10961
11080
|
*)
|
|
10962
11081
|
*/
|
|
10963
|
-
//# sourceMappingURL=chunk-
|
|
11082
|
+
//# sourceMappingURL=chunk-QC5B4ITM.js.map
|