@myop/sdk 0.4.0-beta.2 → 0.4.0-beta.4
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/bundled-declarations.d.ts +72 -32
- package/dist/cjs/{_IframeSDK.a43cfa7d.js → _IframeSDK.3a412cd4.js} +165 -63
- package/dist/cjs/_IframeSDK.3a412cd4.js.map +7 -0
- package/dist/cjs/_IframeSDK.9338575d.min.js +1 -0
- package/dist/cjs/{_WebComponentSDK.0f6ef369.js → _WebComponentSDK.4b6ac803.js} +121 -18
- package/dist/cjs/_WebComponentSDK.4b6ac803.js.map +7 -0
- package/dist/cjs/_WebComponentSDK.4e78d94a.min.js +1 -0
- package/dist/cjs/_hostSDK.34f770f6.min.js +1 -0
- package/dist/cjs/{_hostSDK.38fcc5b4.js → _hostSDK.e9b70e44.js} +121 -18
- package/dist/cjs/_hostSDK.e9b70e44.js.map +7 -0
- package/dist/cjs/myop_sdk.js +11 -11
- package/dist/cjs/myop_sdk.js.map +1 -1
- package/dist/cjs/myop_sdk.min.js +1 -1
- package/dist/cjs-bundled/myop_sdk.bundled.js +167 -64
- package/dist/cjs-bundled/myop_sdk.bundled.js.map +3 -3
- package/dist/cjs-bundled/myop_sdk.bundled.min.js +1 -1
- package/dist/module/Iframe/IframeSDK.d.ts +26 -30
- package/dist/module/Iframe/index.js +166 -64
- package/dist/module/Iframe/index.js.map +3 -3
- package/dist/module/SDK.js +167 -64
- package/dist/module/SDK.js.map +3 -3
- package/dist/module/embeddedSDK.js +1 -1
- package/dist/module/host/components/IMyopComponent.d.ts +3 -0
- package/dist/module/host/components/myopExtractedWebComponent.d.ts +24 -1
- package/dist/module/host/embeddedSDK.js +1 -1
- package/dist/module/host/index.js +122 -19
- package/dist/module/host/index.js.map +2 -2
- package/dist/module/host/loaders/HTMLComponentLoader.d.ts +19 -1
- package/dist/module/webcomponent/index.js +122 -19
- package/dist/module/webcomponent/index.js.map +2 -2
- package/package.json +1 -1
- package/dist/cjs/_IframeSDK.8f08de4b.min.js +0 -1
- package/dist/cjs/_IframeSDK.a43cfa7d.js.map +0 -7
- package/dist/cjs/_WebComponentSDK.0f6ef369.js.map +0 -7
- package/dist/cjs/_WebComponentSDK.649e5e47.min.js +0 -1
- package/dist/cjs/_hostSDK.131fedc9.min.js +0 -1
- package/dist/cjs/_hostSDK.38fcc5b4.js.map +0 -7
|
@@ -683,7 +683,7 @@ declare module "@myop/sdk/host/componentCore" {
|
|
|
683
683
|
};
|
|
684
684
|
}
|
|
685
685
|
declare module "@myop/sdk/host/components/myopExtractedWebComponent" {
|
|
686
|
-
import { BaseMyopComponent } from "@myop/sdk/host/components/IMyopComponent";
|
|
686
|
+
import { AutoSizeOptions, BaseMyopComponent } from "@myop/sdk/host/components/IMyopComponent";
|
|
687
687
|
import { BaseMyopMessage } from "@myop/sdk/messages";
|
|
688
688
|
import { CleanupHandler, IComponentDefinitionConfig, ISkinConfig } from "@myop/sdk/common";
|
|
689
689
|
import { loaderOptions } from "@myop/sdk/host/hostSDK";
|
|
@@ -703,6 +703,29 @@ declare module "@myop/sdk/host/components/myopExtractedWebComponent" {
|
|
|
703
703
|
private _ctaQueue;
|
|
704
704
|
private _ctaListener;
|
|
705
705
|
constructor(componentDefinition: IComponentDefinitionConfig, skin: ISkinConfig, container: HTMLElement, customElement: HTMLElement, tagName: string, options?: loaderOptions);
|
|
706
|
+
private _autoSizeOptions?;
|
|
707
|
+
/**
|
|
708
|
+
* Data-out for size — the extracted counterpart of the iframe implementation.
|
|
709
|
+
*
|
|
710
|
+
* THIS EXISTED ONLY ON MyopIframeComponent, and that was invisible for as long as
|
|
711
|
+
* minification was sending every v2 component down the iframe path. Restoring v2
|
|
712
|
+
* detection moves them to this class, and `@myop/react` drives both through one
|
|
713
|
+
* wrapper: in content mode it hides its loader from the FIRST onSizeChange, so a
|
|
714
|
+
* component whose class has no observeAutoSize leaves a React host on a loader
|
|
715
|
+
* forever. Pic-Time's sizeMeta is width:100% with no height, which is exactly content
|
|
716
|
+
* mode. Caught by v2-data-contract D8 (API parity).
|
|
717
|
+
*
|
|
718
|
+
* It REPORTS rather than imposes. An iframe has no intrinsic size and must be
|
|
719
|
+
* measured inside and then set from outside; a custom element sizes itself from its
|
|
720
|
+
* own content, so the honest thing is to measure it and tell the host, and let the
|
|
721
|
+
* host decide. React then uses width:100% and the measured height.
|
|
722
|
+
*
|
|
723
|
+
* The one thing it does impose is `display: block` when the element computes to
|
|
724
|
+
* `inline`, which is the default for a custom element and measures as zero height no
|
|
725
|
+
* matter what it contains. The iframe path forces width:100% for the same reason —
|
|
726
|
+
* without it the first measurement is 0 and the host sizes the component to nothing.
|
|
727
|
+
*/
|
|
728
|
+
observeAutoSize: (options?: AutoSizeOptions) => CleanupHandler;
|
|
706
729
|
cleanupInspect: CleanupHandler;
|
|
707
730
|
inspect(): CleanupHandler;
|
|
708
731
|
hide(): void;
|
|
@@ -1262,6 +1285,9 @@ declare module "@myop/sdk/host/components/IMyopComponent" {
|
|
|
1262
1285
|
initiated: () => Promise<any>;
|
|
1263
1286
|
private sendCleanupMessage;
|
|
1264
1287
|
send(message: BaseMyopMessage): CleanupHandler;
|
|
1288
|
+
/** Runs the 'onDispose' handlers once. hostSDK registers the one that unregisters
|
|
1289
|
+
* this component, so it has to run on EVERY dispose path, initiated or not. */
|
|
1290
|
+
private runDisposeHandlers;
|
|
1265
1291
|
dispose(): void;
|
|
1266
1292
|
props: P;
|
|
1267
1293
|
refs: R;
|
|
@@ -1483,41 +1509,37 @@ declare module "@myop/sdk/Iframe/IframeSDK" {
|
|
|
1483
1509
|
messageToHost: (message: MyopBindReplayMessage, replayTo?: MyopBindMessage) => void;
|
|
1484
1510
|
supportedHandlers: BaseMessageHandler[];
|
|
1485
1511
|
/**
|
|
1486
|
-
*
|
|
1487
|
-
*
|
|
1488
|
-
* The host only learns a component exists AFTER `await componentLoader.load(...)`
|
|
1489
|
-
* resolves (hostSDK.ts:433-437 — the line above it already calls this a race). A frame
|
|
1490
|
-
* that boots inside that await announces itself to a host whose components2init is
|
|
1491
|
-
* still empty: the host iterates nothing, sends no InitMessage, and a single-shot
|
|
1492
|
-
* announce leaves the frame waiting forever. The visible symptom is initiated()
|
|
1493
|
-
* rejecting with timeout_<ms>, with exactly one message on the wire -- InitRequest,
|
|
1494
|
-
* never InitResponse.
|
|
1512
|
+
* The window this frame answers to. Defaults to the parent, which is correct for a
|
|
1513
|
+
* component mounted directly by the page's own host.
|
|
1495
1514
|
*
|
|
1496
|
-
*
|
|
1497
|
-
*
|
|
1515
|
+
* IT IS NOT ALWAYS THE PARENT. A host can mount a component into a container that
|
|
1516
|
+
* lives inside ANOTHER component's document -- Pic-Time's reel templates are mounted
|
|
1517
|
+
* by the top-level host into `#reelSlot` inside the reel dialog's frame. The real host
|
|
1518
|
+
* is then a GRANDparent, and `window.parent` is a frame that knows nothing about this
|
|
1519
|
+
* component: its nested hostSDK has no entry for it and its IframeSDK drops messages
|
|
1520
|
+
* from a child. Replies sent to the parent are simply lost.
|
|
1498
1521
|
*
|
|
1499
|
-
*
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
*
|
|
1504
|
-
*
|
|
1505
|
-
* case sends exactly one message.
|
|
1522
|
+
* So we adopt whoever actually talks to us, the first time an ancestor does.
|
|
1523
|
+
*/
|
|
1524
|
+
private hostWindow;
|
|
1525
|
+
/**
|
|
1526
|
+
* Commands (including ExecuteScript) must come from a window this frame is actually
|
|
1527
|
+
* nested inside -- never from an opener or a sibling that merely holds a reference.
|
|
1506
1528
|
*
|
|
1507
|
-
*
|
|
1508
|
-
*
|
|
1509
|
-
*
|
|
1510
|
-
*
|
|
1511
|
-
*
|
|
1512
|
-
*
|
|
1513
|
-
*
|
|
1529
|
+
* THIS USED TO COMPARE AGAINST `window.parent` ALONE, and that was a regression I
|
|
1530
|
+
* shipped in 0.4.0-beta: it silently dropped the host's InitMessage for any component
|
|
1531
|
+
* mounted cross-document, because the host is a grandparent there. The frame then
|
|
1532
|
+
* announced for its full 10s budget into a parent that never answers and rejected with
|
|
1533
|
+
* timeout_10000 having registered nothing. Reproduced live on Pic-Time's QA page
|
|
1534
|
+
* 2026-10-17: 15 InitRequests, 0 InitResponses, `IframeSDK.components` empty.
|
|
1535
|
+
* 0.3.36 had no guard at all, which is why it worked.
|
|
1514
1536
|
*
|
|
1515
|
-
*
|
|
1516
|
-
*
|
|
1517
|
-
*
|
|
1518
|
-
*
|
|
1537
|
+
* Walking the ancestor chain keeps the security property -- an opener or a sibling is
|
|
1538
|
+
* not an ancestor -- while allowing a host any number of frames up. The walk is
|
|
1539
|
+
* bounded, and reading `.parent` across origins is allowed (only the reference is
|
|
1540
|
+
* compared, never touched).
|
|
1519
1541
|
*/
|
|
1520
|
-
private
|
|
1542
|
+
private isFromHostChain;
|
|
1521
1543
|
init: () => void;
|
|
1522
1544
|
}
|
|
1523
1545
|
}
|
|
@@ -2109,7 +2131,25 @@ declare module "@myop/sdk/host/loaders/HTMLComponentLoader" {
|
|
|
2109
2131
|
export class HTMLComponentLoader extends BaseMyopLoader {
|
|
2110
2132
|
type: "HTMLLoader";
|
|
2111
2133
|
/**
|
|
2112
|
-
* Detect if HTML is a Myop Web Component
|
|
2134
|
+
* Detect if HTML is a Myop Web Component.
|
|
2135
|
+
*
|
|
2136
|
+
* THE MARKER COMMENT ALONE IS NOT A SAFE SIGNAL, BECAUSE MINIFIERS DELETE COMMENTS.
|
|
2137
|
+
*
|
|
2138
|
+
* This used to be `startsWith(MYOP_WEB_COMPONENT_MARKER)` and nothing else. The
|
|
2139
|
+
* delivery lane minifies with `removeComments: true` (myop-server derive.ts MIN_CFG),
|
|
2140
|
+
* so the stored `min` artifact of every v2 web component loses its leading
|
|
2141
|
+
* `<!-- myop-web-component -->`. Detection then returned false, the component fell
|
|
2142
|
+
* back to plain iframe rendering, and the whole authored document was shown --
|
|
2143
|
+
* including the v2 authoring wrapper, so Pic-Time's production dashboard displayed the
|
|
2144
|
+
* preview chrome ("Web Component v2", a tag-name badge reading `loading...`, SIZE /
|
|
2145
|
+
* OVERFLOW / BG controls) instead of their component. Reported 2026-10-18; the served
|
|
2146
|
+
* HTML measured 328,071 bytes against 951,130 at origin, with zero `<!--` left in it.
|
|
2147
|
+
*
|
|
2148
|
+
* The script id is the signal that survives, and it is the one `extractWebComponentScript`
|
|
2149
|
+
* already depends on: if that element is present, extraction works, so detection
|
|
2150
|
+
* should agree. Checked with a regex rather than DOMParser -- this runs for every
|
|
2151
|
+
* HTML component, and parsing a 300 KB document to answer a yes/no question is not
|
|
2152
|
+
* worth it.
|
|
2113
2153
|
*/
|
|
2114
2154
|
private isMyopWebComponent;
|
|
2115
2155
|
/**
|
|
@@ -1008,6 +1008,7 @@ var BaseMyopComponent = class {
|
|
|
1008
1008
|
setTimeout(() => {
|
|
1009
1009
|
if (!this.isInitiated && this._whenInitiatedReject && !this._markedForDisposed) {
|
|
1010
1010
|
this._whenInitiatedReject("timeout_".concat(timeout, " ").concat(this.id));
|
|
1011
|
+
this.dispose();
|
|
1011
1012
|
}
|
|
1012
1013
|
}, timeout);
|
|
1013
1014
|
this.initiated().then(() => {
|
|
@@ -1091,19 +1092,27 @@ var BaseMyopComponent = class {
|
|
|
1091
1092
|
};
|
|
1092
1093
|
}
|
|
1093
1094
|
}
|
|
1095
|
+
/** Runs the 'onDispose' handlers once. hostSDK registers the one that unregisters
|
|
1096
|
+
* this component, so it has to run on EVERY dispose path, initiated or not. */
|
|
1097
|
+
runDisposeHandlers() {
|
|
1098
|
+
const onDisposeHandlers = this.messageHandlers["onDispose"];
|
|
1099
|
+
if (onDisposeHandlers) {
|
|
1100
|
+
onDisposeHandlers.forEach((disposeHandler) => {
|
|
1101
|
+
disposeHandler(null);
|
|
1102
|
+
});
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1094
1105
|
dispose() {
|
|
1095
1106
|
this.markedForDisposed = true;
|
|
1096
1107
|
if (!this.isInitiated) {
|
|
1108
|
+
this.runDisposeHandlers();
|
|
1109
|
+
this.messageHandlers = {};
|
|
1110
|
+
this.id += "_disposed";
|
|
1097
1111
|
return;
|
|
1098
1112
|
}
|
|
1099
1113
|
console.log("disposing component", this.id);
|
|
1100
1114
|
this.send(new DisposeMessage());
|
|
1101
|
-
|
|
1102
|
-
if (onDisposeHandlers) {
|
|
1103
|
-
onDisposeHandlers.forEach((disposeHandler) => {
|
|
1104
|
-
disposeHandler(null);
|
|
1105
|
-
});
|
|
1106
|
-
}
|
|
1115
|
+
this.runDisposeHandlers();
|
|
1107
1116
|
this.messageHandlers = {};
|
|
1108
1117
|
this.id += "_disposed";
|
|
1109
1118
|
this.isInitiated = false;
|
|
@@ -1179,6 +1188,67 @@ var MyopExtractedWebComponent = class extends BaseMyopComponent {
|
|
|
1179
1188
|
this._ctaQueue.push({ action: detail.action, payload: detail.payload });
|
|
1180
1189
|
}
|
|
1181
1190
|
});
|
|
1191
|
+
__publicField(this, "_autoSizeOptions");
|
|
1192
|
+
/**
|
|
1193
|
+
* Data-out for size — the extracted counterpart of the iframe implementation.
|
|
1194
|
+
*
|
|
1195
|
+
* THIS EXISTED ONLY ON MyopIframeComponent, and that was invisible for as long as
|
|
1196
|
+
* minification was sending every v2 component down the iframe path. Restoring v2
|
|
1197
|
+
* detection moves them to this class, and `@myop/react` drives both through one
|
|
1198
|
+
* wrapper: in content mode it hides its loader from the FIRST onSizeChange, so a
|
|
1199
|
+
* component whose class has no observeAutoSize leaves a React host on a loader
|
|
1200
|
+
* forever. Pic-Time's sizeMeta is width:100% with no height, which is exactly content
|
|
1201
|
+
* mode. Caught by v2-data-contract D8 (API parity).
|
|
1202
|
+
*
|
|
1203
|
+
* It REPORTS rather than imposes. An iframe has no intrinsic size and must be
|
|
1204
|
+
* measured inside and then set from outside; a custom element sizes itself from its
|
|
1205
|
+
* own content, so the honest thing is to measure it and tell the host, and let the
|
|
1206
|
+
* host decide. React then uses width:100% and the measured height.
|
|
1207
|
+
*
|
|
1208
|
+
* The one thing it does impose is `display: block` when the element computes to
|
|
1209
|
+
* `inline`, which is the default for a custom element and measures as zero height no
|
|
1210
|
+
* matter what it contains. The iframe path forces width:100% for the same reason —
|
|
1211
|
+
* without it the first measurement is 0 and the host sizes the component to nothing.
|
|
1212
|
+
*/
|
|
1213
|
+
__publicField(this, "observeAutoSize", (options) => {
|
|
1214
|
+
this._autoSizeOptions = options;
|
|
1215
|
+
const el = this.customElement;
|
|
1216
|
+
try {
|
|
1217
|
+
if (getComputedStyle(el).display === "inline") el.style.display = "block";
|
|
1218
|
+
} catch (e) {
|
|
1219
|
+
}
|
|
1220
|
+
const parse = (v) => {
|
|
1221
|
+
if (v === void 0) return void 0;
|
|
1222
|
+
if (typeof v === "number") return v;
|
|
1223
|
+
const n = parseFloat(v);
|
|
1224
|
+
return isNaN(n) ? void 0 : n;
|
|
1225
|
+
};
|
|
1226
|
+
const clamp = (v, min, max) => min !== void 0 && v < min ? min : max !== void 0 && v > max ? max : v;
|
|
1227
|
+
const report = () => {
|
|
1228
|
+
var _a;
|
|
1229
|
+
const opts = this._autoSizeOptions;
|
|
1230
|
+
const rect = el.getBoundingClientRect();
|
|
1231
|
+
const autoHeight = (opts == null ? void 0 : opts.explicitHeight) === void 0;
|
|
1232
|
+
(_a = opts == null ? void 0 : opts.onSizeChange) == null ? void 0 : _a.call(opts, {
|
|
1233
|
+
width: rect.width,
|
|
1234
|
+
height: autoHeight ? clamp(rect.height, parse(opts == null ? void 0 : opts.minHeight), parse(opts == null ? void 0 : opts.maxHeight)) : rect.height,
|
|
1235
|
+
autoSizingWidth: false,
|
|
1236
|
+
// a web component fills its container's width
|
|
1237
|
+
autoSizingHeight: autoHeight
|
|
1238
|
+
});
|
|
1239
|
+
};
|
|
1240
|
+
let ro;
|
|
1241
|
+
try {
|
|
1242
|
+
ro = new ResizeObserver(() => report());
|
|
1243
|
+
ro.observe(el);
|
|
1244
|
+
} catch (e) {
|
|
1245
|
+
}
|
|
1246
|
+
report();
|
|
1247
|
+
return () => {
|
|
1248
|
+
ro == null ? void 0 : ro.disconnect();
|
|
1249
|
+
this._autoSizeOptions = void 0;
|
|
1250
|
+
};
|
|
1251
|
+
});
|
|
1182
1252
|
__publicField(this, "cleanupInspect", () => {
|
|
1183
1253
|
});
|
|
1184
1254
|
/**
|
|
@@ -1506,8 +1576,13 @@ var createRefComponent = async (ref, componentConfig, components, options) => {
|
|
|
1506
1576
|
return new Promise(async (resolve, reject) => {
|
|
1507
1577
|
const refComponent = new MyopRefComponent(componentConfig.type, component, options);
|
|
1508
1578
|
components.push(refComponent);
|
|
1579
|
+
const unregister = () => {
|
|
1580
|
+
const at = components.indexOf(refComponent);
|
|
1581
|
+
if (at !== -1) components.splice(at, 1);
|
|
1582
|
+
};
|
|
1509
1583
|
await component.initiated();
|
|
1510
1584
|
const timeout = setTimeout(() => {
|
|
1585
|
+
unregister();
|
|
1511
1586
|
reject("timeout");
|
|
1512
1587
|
}, 5e3);
|
|
1513
1588
|
component.send(new CreateRefComponentMessage(
|
|
@@ -1520,11 +1595,13 @@ var createRefComponent = async (ref, componentConfig, components, options) => {
|
|
|
1520
1595
|
async (message) => {
|
|
1521
1596
|
clearTimeout(timeout);
|
|
1522
1597
|
if (message.failed) {
|
|
1598
|
+
unregister();
|
|
1523
1599
|
reject("CreateRefComponentMessage failed");
|
|
1524
1600
|
return;
|
|
1525
1601
|
}
|
|
1526
1602
|
const implementationComponent = components.find((c) => c.id == refComponent.id);
|
|
1527
|
-
if (implementationComponent !== refComponent) {
|
|
1603
|
+
if (implementationComponent && implementationComponent !== refComponent) {
|
|
1604
|
+
unregister();
|
|
1528
1605
|
component.refs[ref.refConfig.name] = implementationComponent;
|
|
1529
1606
|
resolve(implementationComponent);
|
|
1530
1607
|
return;
|
|
@@ -1888,7 +1965,7 @@ var MyopIframeComponent = class extends BaseMyopComponent {
|
|
|
1888
1965
|
return cleanupHandler;
|
|
1889
1966
|
});
|
|
1890
1967
|
__publicField(this, "dispose", () => {
|
|
1891
|
-
var _a;
|
|
1968
|
+
var _a, _b;
|
|
1892
1969
|
(_a = this._bridgeSizeCleanup) == null ? void 0 : _a.call(this);
|
|
1893
1970
|
if (this._autoSizeCleanup) {
|
|
1894
1971
|
this._autoSizeCleanup();
|
|
@@ -1901,7 +1978,7 @@ var MyopIframeComponent = class extends BaseMyopComponent {
|
|
|
1901
1978
|
if (this.cleanupInspect)
|
|
1902
1979
|
this.cleanupInspect();
|
|
1903
1980
|
super.dispose();
|
|
1904
|
-
this.IframeElement.parentNode.removeChild(this.IframeElement);
|
|
1981
|
+
(_b = this.IframeElement.parentNode) == null ? void 0 : _b.removeChild(this.IframeElement);
|
|
1905
1982
|
});
|
|
1906
1983
|
this.element = this.IframeElement;
|
|
1907
1984
|
this._lastData = options == null ? void 0 : options.data;
|
|
@@ -2173,7 +2250,7 @@ var WebcomponentLoader = class extends BaseMyopLoader {
|
|
|
2173
2250
|
};
|
|
2174
2251
|
|
|
2175
2252
|
// version:myop-sdk-version
|
|
2176
|
-
var myop_sdk_version_default = "0.4.0-beta.
|
|
2253
|
+
var myop_sdk_version_default = "0.4.0-beta.4";
|
|
2177
2254
|
|
|
2178
2255
|
// src/helpers/deliveryStore.ts
|
|
2179
2256
|
var DB_NAME = "myop-delivery";
|
|
@@ -3335,14 +3412,14 @@ var HTMLComponentLoader = class extends BaseMyopLoader {
|
|
|
3335
3412
|
if (!isolate) {
|
|
3336
3413
|
doc.writeln("".concat(sdkScript).concat(initScript));
|
|
3337
3414
|
}
|
|
3338
|
-
if (!isolate && (options == null ? void 0 : options.data) !== void 0) {
|
|
3415
|
+
if (!isolate && (options == null ? void 0 : options.data) !== void 0 && (options == null ? void 0 : options.data) !== null) {
|
|
3339
3416
|
const ctaQueueScript = "<script>\n window.__myop_cta_queue = [];\n window.myop_cta_handler = function(action, payload) {\n window.__myop_cta_queue.push({action: action, payload: payload});\n };\n <\/script>";
|
|
3340
3417
|
const escapedData = JSON.stringify(options.data).replace(/<\/script/gi, "\\u003c/script");
|
|
3341
3418
|
const dataScript = '<script id="__myop_init_data_script">\n (function() {\n var data = '.concat(escapedData, ";\n var scriptEl = document.getElementById('__myop_init_data_script');\n\n var callInitInterface = function(fn) {\n var startTime = performance.now();\n // Mark as pre-injected data call for extension tracking\n window.__myop_init_preinjected = true;\n try {\n fn(data);\n } finally {\n window.__myop_init_preinjected = false;\n }\n var duration = performance.now() - startTime;\n\n // Notify extension via postMessage (init* = pre-injected)\n try {\n window.parent.postMessage({\n type: 'MYOP_PREINJECTED_INIT',\n payload: {\n data: data,\n duration: duration,\n timestamp: Date.now()\n }\n }, '*');\n } catch (e) {}\n\n // Cleanup: remove script tag and stored data\n if (scriptEl && scriptEl.parentNode) {\n scriptEl.parentNode.removeChild(scriptEl);\n }\n delete window.__myop_init_data;\n };\n\n if (typeof window.myop_init_interface === 'function') {\n // Function already exists - call immediately\n callInitInterface(window.myop_init_interface);\n } else {\n // Function not yet defined - store data and watch for definition\n window.__myop_init_data = data;\n\n var _original;\n Object.defineProperty(window, 'myop_init_interface', {\n configurable: true,\n enumerable: true,\n set: function(fn) {\n _original = fn;\n if (window.__myop_init_data !== undefined) {\n var storedData = window.__myop_init_data;\n delete window.__myop_init_data;\n callInitInterface(fn);\n }\n },\n get: function() {\n return _original;\n }\n });\n }\n })();\n <\/script>");
|
|
3342
3419
|
doc.writeln(ctaQueueScript + dataScript);
|
|
3343
3420
|
}
|
|
3344
3421
|
if (isolate) {
|
|
3345
|
-
const escapedData = (options == null ? void 0 : options.data) !== void 0 ? JSON.stringify(options.data).replace(/<\/script/gi, "\\u003c/script") : "undefined";
|
|
3422
|
+
const escapedData = (options == null ? void 0 : options.data) !== void 0 && (options == null ? void 0 : options.data) !== null ? JSON.stringify(options.data).replace(/<\/script/gi, "\\u003c/script") : "undefined";
|
|
3346
3423
|
doc.writeln(this.buildIsolationBridge(escapedData));
|
|
3347
3424
|
}
|
|
3348
3425
|
doc.close();
|
|
@@ -3397,10 +3474,29 @@ var HTMLComponentLoader = class extends BaseMyopLoader {
|
|
|
3397
3474
|
});
|
|
3398
3475
|
}
|
|
3399
3476
|
/**
|
|
3400
|
-
* Detect if HTML is a Myop Web Component
|
|
3477
|
+
* Detect if HTML is a Myop Web Component.
|
|
3478
|
+
*
|
|
3479
|
+
* THE MARKER COMMENT ALONE IS NOT A SAFE SIGNAL, BECAUSE MINIFIERS DELETE COMMENTS.
|
|
3480
|
+
*
|
|
3481
|
+
* This used to be `startsWith(MYOP_WEB_COMPONENT_MARKER)` and nothing else. The
|
|
3482
|
+
* delivery lane minifies with `removeComments: true` (myop-server derive.ts MIN_CFG),
|
|
3483
|
+
* so the stored `min` artifact of every v2 web component loses its leading
|
|
3484
|
+
* `<!-- myop-web-component -->`. Detection then returned false, the component fell
|
|
3485
|
+
* back to plain iframe rendering, and the whole authored document was shown --
|
|
3486
|
+
* including the v2 authoring wrapper, so Pic-Time's production dashboard displayed the
|
|
3487
|
+
* preview chrome ("Web Component v2", a tag-name badge reading `loading...`, SIZE /
|
|
3488
|
+
* OVERFLOW / BG controls) instead of their component. Reported 2026-10-18; the served
|
|
3489
|
+
* HTML measured 328,071 bytes against 951,130 at origin, with zero `<!--` left in it.
|
|
3490
|
+
*
|
|
3491
|
+
* The script id is the signal that survives, and it is the one `extractWebComponentScript`
|
|
3492
|
+
* already depends on: if that element is present, extraction works, so detection
|
|
3493
|
+
* should agree. Checked with a regex rather than DOMParser -- this runs for every
|
|
3494
|
+
* HTML component, and parsing a 300 KB document to answer a yes/no question is not
|
|
3495
|
+
* worth it.
|
|
3401
3496
|
*/
|
|
3402
3497
|
isMyopWebComponent(html) {
|
|
3403
|
-
|
|
3498
|
+
if (html.trimStart().startsWith(MYOP_WEB_COMPONENT_MARKER)) return true;
|
|
3499
|
+
return new RegExp("<script[^>]*\\sid=[\"']?".concat(MYOP_WEB_COMPONENT_SCRIPT_ID, "[\"'\\s>]"), "i").test(html);
|
|
3404
3500
|
}
|
|
3405
3501
|
/**
|
|
3406
3502
|
* Resolve how this component should render, applying the host render policy.
|
|
@@ -3495,7 +3591,6 @@ var HTMLComponentLoader = class extends BaseMyopLoader {
|
|
|
3495
3591
|
if (!container.querySelector('[id^="myop-comp-"]')) {
|
|
3496
3592
|
container.innerHTML = "";
|
|
3497
3593
|
}
|
|
3498
|
-
this.appendChild(container, element, options);
|
|
3499
3594
|
const component = new MyopExtractedWebComponent(
|
|
3500
3595
|
componentDefinition,
|
|
3501
3596
|
skin,
|
|
@@ -3504,7 +3599,8 @@ var HTMLComponentLoader = class extends BaseMyopLoader {
|
|
|
3504
3599
|
tagName,
|
|
3505
3600
|
options
|
|
3506
3601
|
);
|
|
3507
|
-
|
|
3602
|
+
this.appendChild(container, element, options);
|
|
3603
|
+
if ((options == null ? void 0 : options.data) !== void 0 && (options == null ? void 0 : options.data) !== null) {
|
|
3508
3604
|
component.initWithData(options.data);
|
|
3509
3605
|
}
|
|
3510
3606
|
console.log("[MyopWebComponent] Loaded <".concat(tagName, "> into container"));
|
|
@@ -3629,7 +3725,12 @@ var HostSDK = class {
|
|
|
3629
3725
|
}
|
|
3630
3726
|
const myopMessage = event.data;
|
|
3631
3727
|
if (myopMessage.messageType === MyopMessages.InitRequest) {
|
|
3632
|
-
this.components2init.
|
|
3728
|
+
const fromAskingFrame = this.components2init.filter((component2) => {
|
|
3729
|
+
const element = component2.element;
|
|
3730
|
+
return (element == null ? void 0 : element.nodeName) === "IFRAME" && element.contentWindow === event.source;
|
|
3731
|
+
});
|
|
3732
|
+
const targets = fromAskingFrame.length ? fromAskingFrame : this.components2init;
|
|
3733
|
+
targets.forEach((component2) => {
|
|
3633
3734
|
component2.send(new InitMessage(component2.id));
|
|
3634
3735
|
});
|
|
3635
3736
|
return;
|
|
@@ -3846,7 +3947,6 @@ var HostSDK = class {
|
|
|
3846
3947
|
let component = await componentLoader.load(componentConfig.type, selectedSkin, container, options);
|
|
3847
3948
|
if (!component.isInitiated) {
|
|
3848
3949
|
this.components2init.push(component);
|
|
3849
|
-
component.send(new InitMessage(component.id));
|
|
3850
3950
|
}
|
|
3851
3951
|
component.bind("onDispose", () => {
|
|
3852
3952
|
this.components = this.components.filter((c) => c !== component);
|
|
@@ -3854,6 +3954,9 @@ var HostSDK = class {
|
|
|
3854
3954
|
return true;
|
|
3855
3955
|
});
|
|
3856
3956
|
this.components.push(component);
|
|
3957
|
+
if (!component.isInitiated) {
|
|
3958
|
+
component.send(new InitMessage(component.id));
|
|
3959
|
+
}
|
|
3857
3960
|
if (!options || options.connectProps !== false) {
|
|
3858
3961
|
component = connectProps(component, componentConfig.type.props, selectedSkin);
|
|
3859
3962
|
}
|
|
@@ -4107,7 +4210,7 @@ var IframeSDK = class extends BaseMyopSDK {
|
|
|
4107
4210
|
super();
|
|
4108
4211
|
__publicField(this, "version", myop_sdk_version_default);
|
|
4109
4212
|
__publicField(this, "messageToHost", (message, replayTo) => {
|
|
4110
|
-
var _a;
|
|
4213
|
+
var _a, _b;
|
|
4111
4214
|
if (!message.replayToHandler && (replayTo == null ? void 0 : replayTo.handlerUniqueId))
|
|
4112
4215
|
message.replayToHandler = replayTo.handlerUniqueId;
|
|
4113
4216
|
const componentIds = Object.keys(this.components);
|
|
@@ -4121,7 +4224,7 @@ var IframeSDK = class extends BaseMyopSDK {
|
|
|
4121
4224
|
if (!message.source)
|
|
4122
4225
|
message.source = componentIds[0];
|
|
4123
4226
|
message.direction = MessageDirection.up;
|
|
4124
|
-
(_a = window.parent) == null ? void 0 :
|
|
4227
|
+
(_b = (_a = this.hostWindow) != null ? _a : window.parent) == null ? void 0 : _b.postMessage(__spreadValues({}, message), "*");
|
|
4125
4228
|
});
|
|
4126
4229
|
__publicField(this, "supportedHandlers", [
|
|
4127
4230
|
new ChangeTextHandler(this.messageToHost),
|
|
@@ -4139,59 +4242,58 @@ var IframeSDK = class extends BaseMyopSDK {
|
|
|
4139
4242
|
new CreateRefComponentMessageHandler2(this.messageToHost)
|
|
4140
4243
|
]);
|
|
4141
4244
|
/**
|
|
4142
|
-
*
|
|
4143
|
-
*
|
|
4144
|
-
* The host only learns a component exists AFTER `await componentLoader.load(...)`
|
|
4145
|
-
* resolves (hostSDK.ts:433-437 — the line above it already calls this a race). A frame
|
|
4146
|
-
* that boots inside that await announces itself to a host whose components2init is
|
|
4147
|
-
* still empty: the host iterates nothing, sends no InitMessage, and a single-shot
|
|
4148
|
-
* announce leaves the frame waiting forever. The visible symptom is initiated()
|
|
4149
|
-
* rejecting with timeout_<ms>, with exactly one message on the wire -- InitRequest,
|
|
4150
|
-
* never InitResponse.
|
|
4151
|
-
*
|
|
4152
|
-
* Minification made it common rather than rare: a smaller payload boots the frame
|
|
4153
|
-
* sooner, landing it deeper inside the window. Reported by a customer 2026-10-17.
|
|
4245
|
+
* The window this frame answers to. Defaults to the parent, which is correct for a
|
|
4246
|
+
* component mounted directly by the page's own host.
|
|
4154
4247
|
*
|
|
4155
|
-
*
|
|
4156
|
-
*
|
|
4157
|
-
*
|
|
4158
|
-
*
|
|
4248
|
+
* IT IS NOT ALWAYS THE PARENT. A host can mount a component into a container that
|
|
4249
|
+
* lives inside ANOTHER component's document -- Pic-Time's reel templates are mounted
|
|
4250
|
+
* by the top-level host into `#reelSlot` inside the reel dialog's frame. The real host
|
|
4251
|
+
* is then a GRANDparent, and `window.parent` is a frame that knows nothing about this
|
|
4252
|
+
* component: its nested hostSDK has no entry for it and its IframeSDK drops messages
|
|
4253
|
+
* from a child. Replies sent to the parent are simply lost.
|
|
4159
4254
|
*
|
|
4160
|
-
*
|
|
4161
|
-
|
|
4255
|
+
* So we adopt whoever actually talks to us, the first time an ancestor does.
|
|
4256
|
+
*/
|
|
4257
|
+
__publicField(this, "hostWindow", window.parent);
|
|
4258
|
+
/**
|
|
4259
|
+
* Commands (including ExecuteScript) must come from a window this frame is actually
|
|
4260
|
+
* nested inside -- never from an opener or a sibling that merely holds a reference.
|
|
4162
4261
|
*
|
|
4163
|
-
*
|
|
4164
|
-
*
|
|
4165
|
-
*
|
|
4166
|
-
*
|
|
4167
|
-
*
|
|
4168
|
-
*
|
|
4169
|
-
*
|
|
4262
|
+
* THIS USED TO COMPARE AGAINST `window.parent` ALONE, and that was a regression I
|
|
4263
|
+
* shipped in 0.4.0-beta: it silently dropped the host's InitMessage for any component
|
|
4264
|
+
* mounted cross-document, because the host is a grandparent there. The frame then
|
|
4265
|
+
* announced for its full 10s budget into a parent that never answers and rejected with
|
|
4266
|
+
* timeout_10000 having registered nothing. Reproduced live on Pic-Time's QA page
|
|
4267
|
+
* 2026-10-17: 15 InitRequests, 0 InitResponses, `IframeSDK.components` empty.
|
|
4268
|
+
* 0.3.36 had no guard at all, which is why it worked.
|
|
4170
4269
|
*
|
|
4171
|
-
*
|
|
4172
|
-
*
|
|
4173
|
-
*
|
|
4174
|
-
*
|
|
4270
|
+
* Walking the ancestor chain keeps the security property -- an opener or a sibling is
|
|
4271
|
+
* not an ancestor -- while allowing a host any number of frames up. The walk is
|
|
4272
|
+
* bounded, and reading `.parent` across origins is allowed (only the reference is
|
|
4273
|
+
* compared, never touched).
|
|
4175
4274
|
*/
|
|
4176
|
-
__publicField(this, "
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
tick();
|
|
4275
|
+
__publicField(this, "isFromHostChain", (source) => {
|
|
4276
|
+
if (!source) return true;
|
|
4277
|
+
if (source === window) return true;
|
|
4278
|
+
let current = window;
|
|
4279
|
+
for (let depth = 0; depth < 32; depth++) {
|
|
4280
|
+
const parent = current.parent;
|
|
4281
|
+
if (!parent || parent === current) return false;
|
|
4282
|
+
if (source === parent) return true;
|
|
4283
|
+
current = parent;
|
|
4284
|
+
}
|
|
4285
|
+
return false;
|
|
4188
4286
|
});
|
|
4189
4287
|
__publicField(this, "init", () => {
|
|
4190
4288
|
window.onmessage = (event) => {
|
|
4191
|
-
|
|
4289
|
+
var _a;
|
|
4290
|
+
if (!this.isFromHostChain(event.source)) return;
|
|
4291
|
+
if (event.source && event.source !== window && ((_a = event.data) == null ? void 0 : _a.messageType) === MyopMessages.InitMessage) {
|
|
4292
|
+
this.hostWindow = event.source;
|
|
4293
|
+
}
|
|
4192
4294
|
this.handleIncomeMessages(event);
|
|
4193
4295
|
};
|
|
4194
|
-
this.
|
|
4296
|
+
this.messageToHost(new InitRequest());
|
|
4195
4297
|
});
|
|
4196
4298
|
if (!window.myop) {
|
|
4197
4299
|
window.myop = {};
|