@myop/sdk 0.3.34 → 0.4.0-beta.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.
Files changed (47) hide show
  1. package/dist/bundled-declarations.d.ts +408 -167
  2. package/dist/cjs/{_IframeSDK.c7926c56.js → _IframeSDK.096d8320.js} +1328 -917
  3. package/dist/cjs/_IframeSDK.096d8320.js.map +7 -0
  4. package/dist/cjs/_IframeSDK.8e791f92.min.js +1 -0
  5. package/dist/cjs/_MyopHelpers.91004b19.min.js +1 -0
  6. package/dist/cjs/{_MyopHelpers.08d1cf5d.js → _MyopHelpers.efed13e4.js} +362 -2
  7. package/dist/cjs/_MyopHelpers.efed13e4.js.map +7 -0
  8. package/dist/cjs/_WebComponentSDK.4a87f826.min.js +1 -0
  9. package/dist/cjs/{_WebComponentSDK.501a71f9.js → _WebComponentSDK.e9e877db.js} +886 -478
  10. package/dist/cjs/_WebComponentSDK.e9e877db.js.map +7 -0
  11. package/dist/cjs/{_hostSDK.cdbc1f60.js → _hostSDK.0c271647.js} +1349 -941
  12. package/dist/cjs/_hostSDK.0c271647.js.map +7 -0
  13. package/dist/cjs/_hostSDK.7d759290.min.js +1 -0
  14. package/dist/cjs/myop_sdk.js +35 -24
  15. package/dist/cjs/myop_sdk.js.map +2 -2
  16. package/dist/cjs/myop_sdk.min.js +1 -1
  17. package/dist/cjs-bundled/myop_sdk.bundled.js +1822 -1380
  18. package/dist/cjs-bundled/myop_sdk.bundled.js.map +4 -4
  19. package/dist/cjs-bundled/myop_sdk.bundled.min.js +1 -1
  20. package/dist/module/Iframe/index.js +1095 -672
  21. package/dist/module/Iframe/index.js.map +4 -4
  22. package/dist/module/SDK.d.ts +5 -1
  23. package/dist/module/SDK.js +1980 -1528
  24. package/dist/module/SDK.js.map +4 -4
  25. package/dist/module/embeddedSDK.js +1 -1
  26. package/dist/module/helpers/CloudRepository.d.ts +96 -0
  27. package/dist/module/helpers/cloudRepositoryHelpers.d.ts +99 -0
  28. package/dist/module/helpers/deliveryStore.d.ts +51 -0
  29. package/dist/module/helpers/index.js +370 -3
  30. package/dist/module/helpers/index.js.map +4 -4
  31. package/dist/module/host/components/myopIframeComponent.d.ts +6 -0
  32. package/dist/module/host/embeddedSDK.js +1 -1
  33. package/dist/module/host/hostSDK.d.ts +2 -0
  34. package/dist/module/host/index.js +1065 -645
  35. package/dist/module/host/index.js.map +4 -4
  36. package/dist/module/host/loaders/HTMLComponentLoader.d.ts +2 -1
  37. package/dist/module/webcomponent/index.js +1136 -716
  38. package/dist/module/webcomponent/index.js.map +4 -4
  39. package/package.json +1 -1
  40. package/dist/cjs/_IframeSDK.c2225e19.min.js +0 -1
  41. package/dist/cjs/_IframeSDK.c7926c56.js.map +0 -7
  42. package/dist/cjs/_MyopHelpers.08d1cf5d.js.map +0 -7
  43. package/dist/cjs/_MyopHelpers.59521d8f.min.js +0 -1
  44. package/dist/cjs/_WebComponentSDK.0caec562.min.js +0 -1
  45. package/dist/cjs/_WebComponentSDK.501a71f9.js.map +0 -7
  46. package/dist/cjs/_hostSDK.02edf3e2.min.js +0 -1
  47. package/dist/cjs/_hostSDK.cdbc1f60.js.map +0 -7
@@ -1672,9 +1672,7 @@ var MyopIframeComponent = class extends BaseMyopComponent {
1672
1672
  const iframe = this.IframeElement;
1673
1673
  const doc = iframe.contentDocument;
1674
1674
  if (!doc) {
1675
- console.warn("[MyopIframeComponent] Cannot observe auto size: contentDocument not accessible");
1676
- return () => {
1677
- };
1675
+ return this.observeHeightViaBridge();
1678
1676
  }
1679
1677
  const clamp = (value, min, max) => {
1680
1678
  if (min !== void 0 && value < min) return min;
@@ -1818,6 +1816,32 @@ var MyopIframeComponent = class extends BaseMyopComponent {
1818
1816
  this._autoSizeCleanup = cleanup;
1819
1817
  return cleanup;
1820
1818
  });
1819
+ __publicField(this, "_bridgeSizeCleanup");
1820
+ /**
1821
+ * Auto-height for a frame the host can't read (cross-origin). Runs after the IframeSDK
1822
+ * handshake: the frame reports body resizes and the host re-reads the scroll height.
1823
+ */
1824
+ __publicField(this, "observeHeightViaBridge", () => {
1825
+ if (this._bridgeSizeCleanup) return this._bridgeSizeCleanup;
1826
+ let observerCleanup;
1827
+ let stopped = false;
1828
+ const cleanup = () => {
1829
+ stopped = true;
1830
+ observerCleanup == null ? void 0 : observerCleanup();
1831
+ this._bridgeSizeCleanup = void 0;
1832
+ };
1833
+ this._bridgeSizeCleanup = cleanup;
1834
+ this.initiated().then(() => {
1835
+ if (stopped || this.markedForDisposed) return;
1836
+ this.IframeElement.style.position = "relative";
1837
+ this.IframeElement.style.inset = "auto";
1838
+ this.IframeElement.style.width = "100%";
1839
+ this.setHeightBasedOnScrollHeight();
1840
+ observerCleanup = this.send(new SetResizeObserverMessage());
1841
+ }).catch(() => {
1842
+ });
1843
+ return cleanup;
1844
+ });
1821
1845
  __publicField(this, "setSizeBasedOnDocumentElement", () => {
1822
1846
  const cleanupHandler = this.send(new ExecuteScriptMessage(() => {
1823
1847
  const { height, width } = document.documentElement.getBoundingClientRect();
@@ -1863,6 +1887,8 @@ var MyopIframeComponent = class extends BaseMyopComponent {
1863
1887
  return cleanupHandler;
1864
1888
  });
1865
1889
  __publicField(this, "dispose", () => {
1890
+ var _a;
1891
+ (_a = this._bridgeSizeCleanup) == null ? void 0 : _a.call(this);
1866
1892
  if (this._autoSizeCleanup) {
1867
1893
  this._autoSizeCleanup();
1868
1894
  this._autoSizeCleanup = void 0;
@@ -2028,7 +2054,11 @@ var MyopIframeLoader = class extends BaseMyopLoader {
2028
2054
  container.innerHTML = "";
2029
2055
  IframeElement = this.appendChild(container, IframeElement, options);
2030
2056
  }
2031
- return new MyopIframeComponent(componentDefinition, skin, container, IframeElement, options);
2057
+ const component = new MyopIframeComponent(componentDefinition, skin, container, IframeElement, options);
2058
+ if (typedLoaderConfig.autoHeight && (options == null ? void 0 : options.autoSize) !== false) {
2059
+ component.observeHeightViaBridge();
2060
+ }
2061
+ return component;
2032
2062
  });
2033
2063
  }
2034
2064
  };
@@ -2142,993 +2172,1344 @@ var WebcomponentLoader = class extends BaseMyopLoader {
2142
2172
  };
2143
2173
 
2144
2174
  // version:myop-sdk-version
2145
- var myop_sdk_version_default = "0.3.34";
2175
+ var myop_sdk_version_default = "0.4.0-beta.0";
2146
2176
 
2147
- // src/webcomponent/messageHandlers/BaseWebComponentMessageHandler.ts
2148
- var BaseWebComponentMessageHandler = class extends BaseContextMessageHandler {
2149
- constructor(messageToHost, context) {
2150
- super(messageToHost);
2151
- this.context = context;
2152
- __publicField(this, "executor", (event) => {
2153
- if (event.destination) {
2154
- const destination = event.destination;
2155
- if (this.context[destination].context) {
2156
- const _messageToHost = (message, replayTo) => {
2157
- this.messageToHost(__spreadProps(__spreadValues({}, message), {
2158
- source: event.destination ? event.destination : event.source,
2159
- destination: event.source ? event.source : event.destination
2160
- }), replayTo);
2161
- };
2162
- return this.innerExecutor(event, this.context[destination].context, _messageToHost);
2163
- } else {
2164
- debugger;
2165
- }
2166
- } else {
2167
- debugger;
2177
+ // src/helpers/deliveryStore.ts
2178
+ var DB_NAME = "myop-delivery";
2179
+ var STORE = "components";
2180
+ var DB_VERSION = 1;
2181
+ var dbPromise = null;
2182
+ function openDb() {
2183
+ if (dbPromise) return dbPromise;
2184
+ dbPromise = new Promise((resolve) => {
2185
+ try {
2186
+ if (typeof indexedDB === "undefined") return resolve(null);
2187
+ const req = indexedDB.open(DB_NAME, DB_VERSION);
2188
+ req.onupgradeneeded = () => {
2189
+ const db = req.result;
2190
+ if (!db.objectStoreNames.contains(STORE)) db.createObjectStore(STORE, { keyPath: "key" });
2191
+ };
2192
+ req.onsuccess = () => resolve(req.result);
2193
+ req.onerror = () => resolve(null);
2194
+ req.onblocked = () => resolve(null);
2195
+ } catch (e) {
2196
+ resolve(null);
2197
+ }
2198
+ });
2199
+ return dbPromise;
2200
+ }
2201
+ function tx(mode, fn) {
2202
+ return openDb().then((db) => new Promise((resolve) => {
2203
+ if (!db) return resolve(null);
2204
+ try {
2205
+ const t = db.transaction(STORE, mode);
2206
+ const req = fn(t.objectStore(STORE));
2207
+ req.onsuccess = () => resolve(req.result);
2208
+ req.onerror = () => resolve(null);
2209
+ t.onabort = () => resolve(null);
2210
+ } catch (e) {
2211
+ resolve(null);
2212
+ }
2213
+ }));
2214
+ }
2215
+ var HINT_PREFIX = "myop.d.";
2216
+ function writeHint(key, tag, expiresAt) {
2217
+ try {
2218
+ localStorage.setItem(HINT_PREFIX + key, JSON.stringify({ t: tag, e: expiresAt }));
2219
+ } catch (e) {
2220
+ }
2221
+ }
2222
+ function clearHint(key) {
2223
+ try {
2224
+ localStorage.removeItem(HINT_PREFIX + key);
2225
+ } catch (e) {
2226
+ }
2227
+ }
2228
+ var memory = /* @__PURE__ */ new Map();
2229
+ var deliveryStore = {
2230
+ key(host, componentId, env, preview, opt) {
2231
+ return "".concat(host, "|").concat(componentId, "|").concat(env, "|").concat(preview ? "preview" : "live", "|").concat(opt || "raw");
2232
+ },
2233
+ async get(key) {
2234
+ const m = memory.get(key);
2235
+ if (m) return m;
2236
+ const e = await tx("readonly", (s) => s.get(key));
2237
+ if (e) {
2238
+ memory.set(key, e);
2239
+ writeHint(key, e.tag, e.storedAt + e.windowMs);
2240
+ }
2241
+ return e;
2242
+ },
2243
+ async put(entry) {
2244
+ memory.set(entry.key, entry);
2245
+ writeHint(entry.key, entry.tag, entry.storedAt + entry.windowMs);
2246
+ await tx("readwrite", (s) => s.put(entry));
2247
+ },
2248
+ async drop(key) {
2249
+ memory.delete(key);
2250
+ clearHint(key);
2251
+ await tx("readwrite", (s) => s.delete(key));
2252
+ },
2253
+ /** Still usable without asking the server? */
2254
+ fresh(e) {
2255
+ return !!e && Date.now() - e.storedAt < e.windowMs;
2256
+ },
2257
+ /**
2258
+ * The window this response licenses: the server's freshness minus what a CDN already
2259
+ * spent. Returns 0 when the server said nothing about caching, which means "do not
2260
+ * hold this" — the safe reading, and what production sends today.
2261
+ */
2262
+ windowFrom(headers) {
2263
+ const cc = headers.get("cache-control") || "";
2264
+ if (/no-store|no-cache/i.test(cc)) return 0;
2265
+ const m = /s-maxage\s*=\s*(\d+)/i.exec(cc) || /max-age\s*=\s*(\d+)/i.exec(cc);
2266
+ if (!m) return 0;
2267
+ const total = Number(m[1]);
2268
+ const age = Number(headers.get("age") || 0);
2269
+ return Math.max(0, total - (isNaN(age) ? 0 : age)) * 1e3;
2270
+ },
2271
+ /** For tests and for `refresh()`: forget everything this browser holds. */
2272
+ async clear() {
2273
+ memory.clear();
2274
+ try {
2275
+ for (let i = localStorage.length - 1; i >= 0; i--) {
2276
+ const k = localStorage.key(i);
2277
+ if (k && k.startsWith(HINT_PREFIX)) localStorage.removeItem(k);
2168
2278
  }
2169
- });
2279
+ } catch (e) {
2280
+ }
2281
+ await tx("readwrite", (s) => s.clear());
2170
2282
  }
2171
2283
  };
2172
2284
 
2173
- // src/webcomponent/messageHandlers/bindOnClickHandler.ts
2174
- var BindOnClickHandler2 = class extends BaseWebComponentMessageHandler {
2175
- constructor() {
2176
- super(...arguments);
2177
- __publicField(this, "messageType", MyopMessages.BindClickMessage);
2178
- __publicField(this, "innerExecutor", (event, shadowRootRef, messageToHost) => {
2179
- const items = getElementsByRef(event.ref, shadowRootRef.shadowRoot);
2180
- const cleanupTasks = [];
2181
- items.forEach((item) => {
2182
- const handler = () => {
2183
- messageToHost(new ClickReplayMessage(event.handlerUniqueId));
2184
- };
2185
- item.addEventListener("click", handler);
2186
- cleanupTasks.push(() => {
2187
- item.removeEventListener("click", handler);
2188
- });
2189
- });
2190
- return () => {
2191
- cleanupTasks.forEach((task) => task());
2192
- };
2193
- });
2194
- }
2285
+ // src/helpers/CloudRepository.ts
2286
+ var convertV2VariantToComponentConfig = (config) => {
2287
+ return {
2288
+ instance: {
2289
+ id: "auto",
2290
+ componentId: config.componentId,
2291
+ componentName: config.name,
2292
+ skinSelector: {
2293
+ type: "Dedicated",
2294
+ skin: {
2295
+ id: "skin_auto_v2_converted"
2296
+ }
2297
+ }
2298
+ },
2299
+ type: {
2300
+ id: config.id,
2301
+ name: config.name,
2302
+ description: config.description,
2303
+ props: [
2304
+ {
2305
+ id: "in_auto_v2_converted",
2306
+ name: "myop_init_interface",
2307
+ type: "any",
2308
+ behavior: {
2309
+ type: PropConfigBehaviorTypes.code
2310
+ }
2311
+ },
2312
+ {
2313
+ id: "out_auto_v2_converted",
2314
+ name: "myop_cta_handler",
2315
+ type: "any",
2316
+ behavior: {
2317
+ type: PropConfigBehaviorTypes.code
2318
+ }
2319
+ }
2320
+ ],
2321
+ refs: [],
2322
+ skins: [
2323
+ {
2324
+ id: "skin_auto_v2_converted",
2325
+ name: "auto_v2_converted",
2326
+ description: "",
2327
+ //TODO : handle empty :
2328
+ // @ts-ignore
2329
+ loader: config.consume_variant[0].loader
2330
+ }
2331
+ ],
2332
+ defaultSkin: 0
2333
+ },
2334
+ name: config.name
2335
+ };
2195
2336
  };
2196
-
2197
- // src/webcomponent/messageHandlers/changeTextHandler.ts
2198
- var ChangeTextHandler2 = class extends BaseWebComponentMessageHandler {
2199
- constructor() {
2200
- super(...arguments);
2201
- __publicField(this, "messageType", MyopMessages.ChangeTextMessage);
2202
- __publicField(this, "innerExecutor", (event, shadowRootRef) => {
2203
- const items = getElementsByRef(event.ref, shadowRootRef.shadowRoot);
2204
- items.forEach((item) => {
2205
- item.textContent = event.content;
2206
- });
2207
- });
2337
+ var GLOBAL_KEY = "__MYOP_CLOUD_REPOSITORY_MAIN__";
2338
+ var getGlobalMain = () => {
2339
+ if (typeof window !== "undefined") {
2340
+ return window[GLOBAL_KEY];
2208
2341
  }
2209
- };
2210
-
2211
- // src/webcomponent/shadowRootRef.ts
2212
- var ShadowRootRef = class extends RootRef {
2213
- constructor(container, shadowRoot) {
2214
- super(container, shadowRoot);
2215
- this.container = container;
2216
- this.shadowRoot = shadowRoot;
2342
+ if (typeof globalThis !== "undefined") {
2343
+ return globalThis[GLOBAL_KEY];
2217
2344
  }
2345
+ return void 0;
2218
2346
  };
2219
-
2220
- // src/webcomponent/messageHandlers/DetectMyopRefsMessageHandler.ts
2221
- var DetectMyopRefsMessageHandler2 = class extends BaseWebComponentMessageHandler {
2222
- constructor() {
2223
- super(...arguments);
2224
- __publicField(this, "innerExecutor", (event, shadowRootRef) => {
2225
- const allMyopItems = shadowRootRef.shadowRoot.querySelectorAll("[".concat(myopAttributes.id, "]"));
2226
- let myopRefs = [];
2227
- allMyopItems.forEach((item) => {
2228
- const myopId = item.getAttribute(myopAttributes.id);
2229
- if (myopId) {
2230
- myopRefs.push(new MyopElementRef(myopId, item.tagName, item.getBoundingClientRect(), item.offsetTop, item.offsetLeft));
2231
- }
2232
- });
2233
- this.messageToHost(new DetectedMyopRefsMessage(event.handlerUniqueId, myopRefs));
2234
- });
2235
- __publicField(this, "messageType", MyopMessages.DetectMyopRefsMessage);
2347
+ var setGlobalMain = (instance) => {
2348
+ if (typeof window !== "undefined") {
2349
+ window[GLOBAL_KEY] = instance;
2350
+ }
2351
+ if (typeof globalThis !== "undefined") {
2352
+ globalThis[GLOBAL_KEY] = instance;
2236
2353
  }
2237
2354
  };
2238
-
2239
- // src/webcomponent/messageHandlers/setAttributeHandler.ts
2240
- var setAttributeHandler2 = class extends BaseWebComponentMessageHandler {
2241
- constructor() {
2242
- super(...arguments);
2243
- __publicField(this, "messageType", MyopMessages.SetAttributeMessage);
2244
- __publicField(this, "innerExecutor", (event, shadowRootRef) => {
2245
- const items = getElementsByRef(event.ref, shadowRootRef.shadowRoot);
2246
- items.forEach((item) => {
2247
- item.setAttribute(event.name, event.value);
2248
- });
2355
+ var _CloudRepository = class _CloudRepository {
2356
+ constructor(_baseUrl = "https://cloud.myop.dev") {
2357
+ this._baseUrl = _baseUrl;
2358
+ // v2 cache - keyed by componentId:env:preview|live
2359
+ // Each Promise is tagged with a `resolved` property after settling (see fetchComponentV2).
2360
+ __publicField(this, "variants", {});
2361
+ // Track which components were preloaded and with what params
2362
+ // Key: componentId, Value: { env, preview }
2363
+ __publicField(this, "preloadedComponents", {});
2364
+ // v1 cache
2365
+ __publicField(this, "userFlows", {});
2366
+ __publicField(this, "_defaultEnv", "production");
2367
+ __publicField(this, "_envExplicitlySet", false);
2368
+ __publicField(this, "_staticMode", false);
2369
+ __publicField(this, "_queryParams", {});
2370
+ /**
2371
+ * Opt-in delivery settings, set by myop.delivery(). Off means: this file behaves
2372
+ * EXACTLY as it did before — same host, same URL, same everything.
2373
+ */
2374
+ __publicField(this, "_delivery", { minify: false, host: null, cache: false });
2375
+ /**
2376
+ * What actually happened, for myop.delivery.stats(). `fellBack` is the number that
2377
+ * matters: it is how you find out the lane is unhealthy without waiting for a
2378
+ * bug report, because a fallback is INVISIBLE to the page (it still renders).
2379
+ */
2380
+ __publicField(this, "_deliveryStats", {
2381
+ requests: 0,
2382
+ viaDelivery: 0,
2383
+ fellBack: 0,
2384
+ lastError: null,
2385
+ /** where the last loads were answered from — the number the client window exists to move */
2386
+ fromMemory: 0,
2387
+ fromStorage: 0,
2388
+ fromNetwork: 0,
2389
+ /** re-checks after the window ended, and how many were answered "nothing changed" */
2390
+ revalidations: 0,
2391
+ unchanged: 0,
2392
+ bytesSaved: 0
2249
2393
  });
2394
+ /**
2395
+ * Local serves waiting to be reported: componentId -> {env, count}.
2396
+ *
2397
+ * THE MEASUREMENT PROBLEM THIS SOLVES. The better this cache works, the more usage
2398
+ * vanishes: a visitor inside their freshness window renders the page with no request,
2399
+ * so neither the server nor the CDN can see them. Left alone, a successful rollout
2400
+ * looks in the ledger exactly like customers leaving.
2401
+ *
2402
+ * So local serves are counted here and reported afterwards, batched, on a path that
2403
+ * never blocks rendering: flushed when the page is hidden (keepalive, so it survives
2404
+ * the tab closing) and opportunistically whenever the SDK is talking to the server
2405
+ * anyway. Nothing is sent while the page is loading, which is the point of the window.
2406
+ */
2407
+ __publicField(this, "_localServes", /* @__PURE__ */ new Map());
2408
+ __publicField(this, "_flushBound", false);
2250
2409
  }
2251
- };
2252
-
2253
- // src/webcomponent/messageHandlers/addEventListenerHandler.ts
2254
- var AddEventListenerHandler2 = class extends BaseWebComponentMessageHandler {
2255
- constructor() {
2256
- super(...arguments);
2257
- __publicField(this, "messageType", MyopMessages.AddEventListenerMessage);
2258
- __publicField(this, "innerExecutor", (event, shadowRootRef, messageToHost) => {
2259
- const items = getElementsByRef(event.ref, shadowRootRef.shadowRoot);
2260
- const cleanupTasks = [];
2261
- items.forEach((item) => {
2262
- const handler = (e) => {
2263
- messageToHost(new EventListenerCallbackMessage(event.handlerUniqueId, makeSerializable(e, event.serializableSkeleton)));
2264
- };
2265
- item.addEventListener(event.type, handler);
2266
- cleanupTasks.push(() => {
2267
- item.removeEventListener(event.type, handler);
2268
- });
2269
- });
2270
- return () => {
2271
- cleanupTasks.forEach((task) => task());
2272
- };
2410
+ static get Main() {
2411
+ const globalMain = getGlobalMain();
2412
+ if (globalMain) {
2413
+ return globalMain;
2414
+ }
2415
+ if (!_CloudRepository._main) {
2416
+ _CloudRepository._main = new _CloudRepository();
2417
+ setGlobalMain(_CloudRepository._main);
2418
+ }
2419
+ return _CloudRepository._main;
2420
+ }
2421
+ /**
2422
+ * How big the component we are NOT re-downloading is. Deliberately measured from what
2423
+ * we hold rather than from Content-Length: once a response is compressed the header is
2424
+ * not readable cross-origin, so reading it there reported 0 every single time.
2425
+ */
2426
+ static sizeOfHeld(e) {
2427
+ var _a, _b, _c, _d, _e;
2428
+ try {
2429
+ return (((_e = (_d = (_c = (_b = (_a = e == null ? void 0 : e.config) == null ? void 0 : _a.type) == null ? void 0 : _b.skins) == null ? void 0 : _c[0]) == null ? void 0 : _d.loader) == null ? void 0 : _e.HTML) || "").length;
2430
+ } catch (e2) {
2431
+ return 0;
2432
+ }
2433
+ }
2434
+ /**
2435
+ * Turn on the opt-in delivery path.
2436
+ *
2437
+ * Opting in IS choosing the delivery stack: this points /consume at `host` (a second,
2438
+ * complete stack reading the same data) and asks for the minified artifact. Leave this
2439
+ * uncalled and nothing about the SDK changes.
2440
+ *
2441
+ * Deliberately mutates THIS instance rather than constructing a new repository:
2442
+ * CloudRepository.Main is a global singleton shared across bundles (see getGlobalMain),
2443
+ * so a second instance would be ignored by every component already using the first.
2444
+ */
2445
+ setDelivery(opts = {}) {
2446
+ if (opts.minify !== void 0) this._delivery.minify = !!opts.minify;
2447
+ if (opts.host !== void 0) this._delivery.host = opts.host;
2448
+ if (opts.cache !== void 0) this._delivery.cache = !!opts.cache;
2449
+ }
2450
+ deliveryStats() {
2451
+ var _a;
2452
+ return __spreadProps(__spreadValues({}, this._deliveryStats), {
2453
+ host: (_a = this._delivery.host) != null ? _a : _CloudRepository.DEFAULT_HOST,
2454
+ minify: this._delivery.minify,
2455
+ cache: this._delivery.cache
2273
2456
  });
2274
2457
  }
2275
- };
2276
-
2277
- // src/webcomponent/messageHandlers/CreateRefComponentMessageHandler.ts
2278
- var CreateRefComponentMessageHandler = class extends BaseWebComponentMessageHandler {
2279
- constructor() {
2280
- super(...arguments);
2281
- __publicField(this, "messageType", MyopMessages.CreateRefComponentMessage);
2282
- __publicField(this, "innerExecutor", (event, shadowRootRef, messageToHost) => {
2283
- const createRef = async () => {
2284
- const allItems = shadowRootRef.shadowRoot.querySelectorAll("[".concat(myopAttributes.id, "='").concat(event.refConfig.selector, "']"));
2285
- if (allItems.length === 1) {
2286
- const hostSDK2 = HostSDK.Instance();
2287
- const refComponent = hostSDK2.components.find((c) => c.id === event.options.id);
2288
- if (refComponent) {
2289
- refComponent.dispose();
2290
- } else {
2291
- }
2292
- const index = hostSDK2.components.indexOf(refComponent);
2293
- if (index !== -1) {
2294
- hostSDK2.components.splice(index, 1);
2295
- } else {
2296
- }
2297
- const implementationComponent = await hostSDK2.loadComponent(
2298
- event.nestedComponentConfig,
2299
- allItems.item(0),
2300
- event.options
2301
- );
2302
- const nestedRefConfig = [];
2303
- Object.keys(implementationComponent.refs).forEach((refName) => {
2304
- let ref = implementationComponent.refs[refName];
2305
- ref = ref.__nonProxied || ref;
2306
- if (ref.componentDefinition) {
2307
- nestedRefConfig.push({
2308
- refName,
2309
- componentDefinitionId: ref.componentDefinition.id
2310
- });
2311
- }
2312
- });
2313
- messageToHost(new RefComponentCreatedMessage(event.handlerUniqueId, nestedRefConfig));
2314
- } else {
2315
- console.error("CreateRefComponentMessageHandler - DOM element not found");
2316
- this.messageToHost(new RefComponentCreatedMessage(event.handlerUniqueId, [], true));
2317
- debugger;
2318
- }
2319
- };
2320
- createRef().then();
2321
- });
2458
+ /**
2459
+ * Forget what this browser holds, so the next load asks the server.
2460
+ * `refresh()` with no ids drops everything; the counterpart to the window for the case
2461
+ * where a change has to be visible on THIS load — right after someone publishes.
2462
+ */
2463
+ async refreshDelivery() {
2464
+ await deliveryStore.clear();
2465
+ }
2466
+ noteLocalServe(componentId, env) {
2467
+ const cur = this._localServes.get(componentId);
2468
+ if (cur) cur.count++;
2469
+ else this._localServes.set(componentId, { env, count: 1 });
2470
+ this.bindFlush();
2471
+ }
2472
+ /** Report on page-hide, which is the only event that reliably fires on mobile. */
2473
+ bindFlush() {
2474
+ if (this._flushBound || typeof document === "undefined") return;
2475
+ this._flushBound = true;
2476
+ const onHide = () => {
2477
+ if (document.visibilityState === "hidden") this.flushLocalServes();
2478
+ };
2479
+ document.addEventListener("visibilitychange", onHide);
2480
+ window.addEventListener("pagehide", () => this.flushLocalServes());
2322
2481
  }
2323
- };
2324
-
2325
- // src/webcomponent/messageHandlers/executeScriptHandler.ts
2326
- var ExecuteScriptHandler2 = class extends BaseWebComponentMessageHandler {
2327
- constructor() {
2328
- super(...arguments);
2329
- __publicField(this, "messageType", MyopMessages.ExecuteScriptMessage);
2330
- __publicField(this, "innerExecutor", (event, shadowRootRef, messageToHost) => {
2331
- const replier = (data) => {
2332
- messageToHost(new ExecuteScriptReplyMessage(event.handlerUniqueId, data));
2333
- };
2334
- ExecuteScriptHandlerExecutor(event, replier, shadowRootRef);
2335
- });
2482
+ /**
2483
+ * Send what we have. `keepalive` is what makes this work at page-hide: the request
2484
+ * outlives the document. Failure is ignored — a lost usage report must never be
2485
+ * visible to a visitor, and the next page load will report its own.
2486
+ */
2487
+ flushLocalServes() {
2488
+ if (!this._localServes.size) return;
2489
+ const batch = [...this._localServes.entries()].map(([id, v]) => "".concat(id, ":").concat(v.env, ":").concat(v.count)).join(",");
2490
+ this._localServes.clear();
2491
+ try {
2492
+ const url = "".concat(this.deliveryHost(), "/clientUsage?b=").concat(encodeURIComponent(batch));
2493
+ fetch(url, { keepalive: true, mode: "cors", credentials: "omit" }).catch(() => {
2494
+ });
2495
+ } catch (e) {
2496
+ }
2336
2497
  }
2337
- };
2338
-
2339
- // src/webcomponent/messageHandlers/GetElementValueHandler.ts
2340
- var getInputValue = (element) => {
2341
- if (!element) return null;
2342
- if (element.tagName === "FORM") {
2343
- const formData = new FormData(element);
2344
- const dataObject = {
2345
- formData: {},
2346
- unmappedData: []
2347
- };
2348
- const elements = element.querySelectorAll("input, select, textarea");
2349
- let autoNameIndexer = -1;
2350
- elements.forEach((element2) => {
2351
- if (typeof element2.value === "string") {
2352
- const myopId = element2.getAttribute(myopAttributes.id);
2353
- let key = element2.getAttribute("name");
2354
- const nodeData = {
2355
- id: element2.id,
2356
- name: key,
2357
- value: getInputValue(element2),
2358
- required: element2.required,
2359
- validity: makeSerializable(element2.validity, true),
2360
- myopId
2361
- };
2362
- if (!key) {
2363
- dataObject.unmappedData.push(nodeData);
2364
- } else {
2365
- if (dataObject.formData[key]) {
2366
- if (dataObject.formData[key].value) {
2367
- dataObject.unmappedData.push(nodeData);
2368
- } else {
2369
- dataObject.unmappedData.push(dataObject.formData[key]);
2370
- dataObject.formData[key] = nodeData;
2371
- }
2372
- } else {
2373
- dataObject.formData[key] = nodeData;
2374
- }
2375
- }
2498
+ /** Where /consume should be fetched from right now. */
2499
+ deliveryHost() {
2500
+ return this._delivery.host || this._baseUrl;
2501
+ }
2502
+ /**
2503
+ * Fetch a delivery URL with ONE fallback to the default host.
2504
+ *
2505
+ * The fallback is the kill switch: break or disable the lane and every client drifts
2506
+ * back to cloud.myop.dev on its own, with no customer deploy. It must therefore be
2507
+ * genuinely silent to the page and genuinely LOUD in stats() — a fallback that nobody
2508
+ * counts is an outage nobody notices.
2509
+ *
2510
+ * Only transport failures and 5xx fall back. A 404 is an answer: the component really
2511
+ * is missing, and retrying elsewhere would only hide it.
2512
+ */
2513
+ async deliveryFetch(path) {
2514
+ var _a;
2515
+ if (this._localServes.size) this.flushLocalServes();
2516
+ const host = this.deliveryHost();
2517
+ const primary = "".concat(host).concat(path);
2518
+ this._deliveryStats.requests++;
2519
+ const usingLane = host !== this._baseUrl;
2520
+ try {
2521
+ const res = await fetch(primary);
2522
+ if (res.ok || res.status < 500 || !usingLane) {
2523
+ if (usingLane && res.ok) this._deliveryStats.viaDelivery++;
2524
+ return res;
2376
2525
  }
2377
- });
2378
- return dataObject;
2526
+ this._deliveryStats.lastError = "HTTP ".concat(res.status, " from ").concat(host);
2527
+ } catch (e) {
2528
+ if (!usingLane) throw e;
2529
+ this._deliveryStats.lastError = String((_a = e == null ? void 0 : e.message) != null ? _a : e);
2530
+ }
2531
+ this._deliveryStats.fellBack++;
2532
+ console.warn("[myop] delivery host failed (".concat(this._deliveryStats.lastError, ") \u2014 falling back to ").concat(this._baseUrl));
2533
+ return await fetch("".concat(this._baseUrl).concat(path));
2379
2534
  }
2380
- if (element.type === "checkbox" || element.type === "radio") {
2381
- return element.checked ? element.value : null;
2535
+ /**
2536
+ * Enable static mode for self-hosted component registries.
2537
+ * In static mode, URLs are constructed as file paths instead of query params:
2538
+ * ${baseUrl}/components/${componentId}/${env}.json
2539
+ */
2540
+ enableStaticMode() {
2541
+ this._staticMode = true;
2382
2542
  }
2383
- if (element.tagName === "INPUT" || element.tagName === "TEXTAREA") {
2384
- return element.value;
2543
+ /**
2544
+ * Set query parameters to append to all fetch URLs (e.g. for CDN cache busting).
2545
+ * Example: `setQueryParams({ lastupdate: '11112026' })`
2546
+ */
2547
+ setQueryParams(params) {
2548
+ this._queryParams = params;
2385
2549
  }
2386
- if (element.tagName === "SELECT") {
2387
- return element.value;
2550
+ /**
2551
+ * Set the default environment for this CloudRepository instance
2552
+ */
2553
+ setEnvironment(env) {
2554
+ this._defaultEnv = env;
2555
+ this._envExplicitlySet = true;
2388
2556
  }
2389
- if (element.isContentEditable) {
2390
- return element.innerText || element.textContent;
2557
+ /**
2558
+ * Get the current default environment
2559
+ */
2560
+ getDefaultEnvironment() {
2561
+ return this._defaultEnv;
2391
2562
  }
2392
- return null;
2393
- };
2394
- var GetElementValueHandler = class extends BaseWebComponentMessageHandler {
2395
- constructor() {
2396
- super(...arguments);
2397
- __publicField(this, "messageType", MyopMessages.GetElementValueMessage);
2398
- __publicField(this, "innerExecutor", (event, shadowRootRef, messageToHost) => {
2399
- const items = getElementsByRef(event.ref, shadowRootRef.shadowRoot);
2400
- items.forEach((item) => {
2401
- messageToHost(new ElementValueReplayMessage(getInputValue(item)), event);
2402
- });
2403
- });
2563
+ // ============ V2 Methods (Default) ============
2564
+ /**
2565
+ * Check if a component is already cached/preloaded (v2)
2566
+ * If env and preview are not provided, checks if ANY version is preloaded
2567
+ */
2568
+ isPreloaded(componentId, env, preview) {
2569
+ if (env !== void 0 || preview !== void 0) {
2570
+ const cacheKey = "".concat(componentId, ":").concat(env || this._defaultEnv, ":").concat(preview ? "preview" : "live");
2571
+ return cacheKey in this.variants;
2572
+ }
2573
+ return componentId in this.preloadedComponents;
2404
2574
  }
2405
- };
2406
-
2407
- // src/webcomponent/messageHandlers/setInnerHtmlHandler.ts
2408
- var SetInnerHtmlHandler = class extends BaseWebComponentMessageHandler {
2409
- constructor() {
2410
- super(...arguments);
2411
- __publicField(this, "messageType", MyopMessages.SetInnerHtml);
2412
- __publicField(this, "innerExecutor", (event, shadowRootRef) => {
2413
- const items = getElementsByRef(event.ref, shadowRootRef.shadowRoot);
2414
- items.forEach((item) => {
2415
- item.innerHTML = event.content;
2416
- });
2417
- });
2575
+ /**
2576
+ * Get the preloaded params for a component (if preloaded)
2577
+ * Returns undefined if not preloaded
2578
+ */
2579
+ getPreloadedParams(componentId) {
2580
+ return this.preloadedComponents[componentId];
2418
2581
  }
2419
- };
2420
-
2421
- // src/webcomponent/WebComponentSDK.ts
2422
- var WebComponentSDK = class extends BaseMyopSDK {
2423
- constructor() {
2424
- super(...arguments);
2425
- __publicField(this, "alwaysPassEnvelopesToHost", true);
2426
- //when new web component created.
2427
- __publicField(this, "connectedCallback", (container, shadow) => {
2428
- container.addEventListener(myopWebcomponentConsts.webcomponent_message_key, (e) => {
2429
- const eventContent = e.data ? e.data : e.detail;
2430
- if (!eventContent || !eventContent.myop) {
2431
- debugger;
2432
- throw new Error("recvied webcomponent_message with wrong type");
2433
- } else {
2434
- const message = eventContent;
2435
- this.handleIncomeMessages(e);
2436
- if (message.messageType === MyopMessages.InitMessage) {
2437
- const newComponentId = message.content.id;
2438
- this.components[newComponentId].context = new ShadowRootRef(container, shadow);
2439
- }
2440
- }
2441
- });
2442
- setTimeout(() => {
2443
- this.messageToHost(new InitRequest());
2444
- }, 1);
2445
- });
2446
- __publicField(this, "messageToHost", (message, replayTo) => {
2447
- message.direction = MessageDirection.up;
2448
- if (!message.replayToHandler && (replayTo == null ? void 0 : replayTo.handlerUniqueId))
2449
- message.replayToHandler = replayTo.handlerUniqueId;
2450
- window.myop.hostSDK.send(message);
2451
- });
2452
- __publicField(this, "supportedHandlers", [
2453
- new ChangeTextHandler2(this.messageToHost, this.components),
2454
- new SetInnerHtmlHandler(this.messageToHost, this.components),
2455
- new BindOnClickHandler2(this.messageToHost, this.components),
2456
- new DetectMyopRefsMessageHandler2(this.messageToHost, this.components),
2457
- new setAttributeHandler2(this.messageToHost, this.components),
2458
- new AddEventListenerHandler2(this.messageToHost, this.components),
2459
- new ExecuteScriptHandler2(this.messageToHost, this.components),
2460
- new GetElementValueHandler(this.messageToHost, this.components),
2461
- //
2462
- new CreateRefComponentMessageHandler(this.messageToHost, this.components)
2463
- ]);
2464
- __publicField(this, "init", () => {
2465
- });
2582
+ /**
2583
+ * Resolve a component config: fetch it once, then serve it from this
2584
+ * repository's cache.
2585
+ *
2586
+ * This is the name to use. `fetchComponentV2` is the same method under its
2587
+ * versioned name and stays for backward compatibility — note that the
2588
+ * unversioned `fetchComponent` is a *deprecated v1* API and is NOT this.
2589
+ *
2590
+ * Completes the trio with {@link getResolvedConfig} (synchronous read of an
2591
+ * already-resolved config) and {@link hydrateComponent} (seed one you
2592
+ * already hold).
2593
+ *
2594
+ * Prefer the module-level `preloadComponents([id])` in app code; reach for
2595
+ * this when you need an isolated repository instance whose cache lifetime
2596
+ * you control — a server with its own TTL, for example.
2597
+ */
2598
+ async resolveComponent(componentId, env, preview) {
2599
+ return this.fetchComponentV2(componentId, env, preview);
2466
2600
  }
2467
- };
2468
- defineDynamicImport("WebComponentSDK", WebComponentSDK);
2469
-
2470
- // src/host/embeddedSDK.ts
2471
- var EMBEDDED_SDK_CONTENT = "";
2472
- var HAS_EMBEDDED_SDK = false;
2473
-
2474
- // src/host/loaders/HTMLComponentLoader.ts
2475
- var MYOP_WEB_COMPONENT_MARKER = "<!-- myop-web-component -->";
2476
- var MYOP_WEB_COMPONENT_SCRIPT_ID = "myop-web-component";
2477
- var extractedWebComponentScripts = /* @__PURE__ */ new Set();
2478
- var seen2 = [];
2479
- var sdk;
2480
- var getInitiatedSdk = async () => {
2481
- if (!sdk) {
2482
- sdk = new WebComponentSDK();
2483
- sdk.init();
2484
- }
2485
- return sdk;
2486
- };
2487
- function createHTMLElementConstructor(htmlString, ShadowRootMode) {
2488
- return class extends HTMLElement {
2489
- connectedCallback() {
2490
- const mode = ShadowRootMode === ShadowRootModeType.open ? "open" : "closed";
2491
- const shadow = this.attachShadow({ mode });
2492
- getInitiatedSdk().then((sdk2) => {
2493
- sdk2.connectedCallback(this, shadow);
2494
- shadow.innerHTML = htmlString;
2495
- });
2496
- }
2497
- };
2498
- }
2499
- var createCustomWebComponent = (componentName, html, ShadowRootMode) => {
2500
- customElements.define(componentName, createHTMLElementConstructor(html, ShadowRootMode));
2501
- console.log("define('".concat(componentName, "') was called, web component ready to use"));
2502
- };
2503
- var toWebComponentName = (str) => {
2504
- return str.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/(\d+)/g, "-$1").toLowerCase();
2505
- };
2506
- var toCustomElementName = (name) => {
2507
- name = toWebComponentName(name);
2508
- return name.replace(/_/g, "-");
2509
- };
2510
- var HTMLComponentLoader = class extends BaseMyopLoader {
2511
- constructor() {
2512
- super(...arguments);
2513
- __publicField(this, "type", SkinLoaders.HTMLLoader);
2514
- __publicField(this, "load", async (componentDefinition, skin, container, options) => {
2515
- const loaderConfig = skin.loader;
2516
- const isWC = this.isMyopWebComponent(loaderConfig.HTML);
2517
- const { mode, isolate } = await this.resolveRenderMode(componentDefinition, skin, loaderConfig, isWC, options);
2518
- if (mode === false) {
2519
- throw new Error('[Myop] Rendering blocked by render policy for component "'.concat(componentDefinition.id, '"'));
2520
- }
2521
- if (isWC) {
2522
- if (mode !== "webComponent") {
2523
- throw new Error('[Myop] Web-component rendering denied by render policy for component "'.concat(componentDefinition.id, '"'));
2524
- }
2525
- return this.loadExtractedWebComponent(componentDefinition, skin, container, loaderConfig.HTML, options);
2601
+ /**
2602
+ * Fetch a v2 component config
2603
+ * If env/preview not specified and component was preloaded, uses preloaded params
2604
+ *
2605
+ * @see resolveComponent — the unversioned name for this method
2606
+ */
2607
+ async fetchComponentV2(componentId, environmentIdentifier, preview) {
2608
+ const { env, usePreview, cacheKey } = this.resolveParams(componentId, environmentIdentifier, preview);
2609
+ console.log("[CloudRepository] fetchComponentV2", {
2610
+ componentId: componentId.slice(0, 8) + "...",
2611
+ env,
2612
+ usePreview,
2613
+ cacheKey,
2614
+ cached: cacheKey in this.variants
2615
+ });
2616
+ if (!this.variants[cacheKey]) {
2617
+ if (!this.preloadedComponents[componentId]) {
2618
+ this.preloadedComponents[componentId] = { env, preview: usePreview };
2526
2619
  }
2527
- let domId = "myop-comp-".concat(uuidv4());
2528
- let element;
2529
- if (loaderConfig.shadowRootMode === ShadowRootModeType.localFrame) {
2530
- const uniqId = uuidv4();
2531
- const domId2 = "myop-comp-".concat(uniqId);
2532
- let IframeElement = document.createElement("iframe");
2533
- IframeElement.id = domId2;
2534
- if (isolate) {
2535
- IframeElement.setAttribute("sandbox", "allow-scripts allow-forms allow-popups");
2536
- }
2537
- IframeElement.style.cssText = "\n display: block;\n padding: 0;\n margin: 0;\n position: absolute;\n inset: 0;\n overflow: hidden;\n border: none;\n opacity: ".concat((options == null ? void 0 : options.hidden) ? "0" : "1", ";\n width: 100%;\n height: 100%;\n pointer-events: ").concat((options == null ? void 0 : options.hidden) ? "none" : "all", ";\n ");
2538
- if (!container.querySelector('[id^="myop-comp-"]'))
2539
- container.innerHTML = "";
2540
- IframeElement = this.appendChild(container, IframeElement, options);
2541
- const __srcParts = [];
2542
- const doc = isolate ? {
2543
- open() {
2544
- },
2545
- writeln(s) {
2546
- __srcParts.push(s);
2547
- },
2548
- close() {
2549
- IframeElement.srcdoc = __srcParts.join("\n");
2550
- }
2551
- } : (() => {
2552
- const d = IframeElement.contentDocument || IframeElement.contentWindow.document;
2553
- d.open();
2554
- return d;
2555
- })();
2556
- let parsedSizeMeta = null;
2557
- {
2558
- const _preParser = new DOMParser();
2559
- const _preDoc = _preParser.parseFromString(loaderConfig.HTML, "text/html");
2560
- const sizeMeta = _preDoc.querySelector('meta[name="myop:size"]');
2561
- if (sizeMeta) {
2562
- try {
2563
- parsedSizeMeta = JSON.parse(sizeMeta.getAttribute("content") || "{}");
2564
- } catch (e) {
2620
+ this.variants[cacheKey] = new Promise(
2621
+ async (resolve, reject) => {
2622
+ try {
2623
+ let url;
2624
+ if (this._staticMode) {
2625
+ const file = usePreview ? "preview" : env;
2626
+ url = "".concat(this._baseUrl, "/components/").concat(componentId, "/").concat(file, ".json");
2627
+ const qs = new URLSearchParams(this._queryParams).toString();
2628
+ if (qs) url += "?".concat(qs);
2629
+ } else {
2630
+ url = "/consume?id=".concat(componentId, "&env=").concat(env);
2631
+ if (usePreview) {
2632
+ url += "&preview=true";
2633
+ }
2634
+ if (this._delivery.minify && !usePreview) {
2635
+ url += "&opt=min";
2636
+ }
2565
2637
  }
2566
- }
2567
- }
2568
- let HTML2Render = loaderConfig.HTML;
2569
- if (options == null ? void 0 : options._inspection) {
2570
- const parser = new DOMParser();
2571
- const _doc = parser.parseFromString(loaderConfig.HTML, "text/html");
2572
- const myop_preview_script = _doc.getElementById("myop_preview");
2573
- myop_preview_script == null ? void 0 : myop_preview_script.removeAttribute("type");
2574
- const temp = _doc.implementation.createHTMLDocument();
2575
- temp.body.innerHTML = "<script>\n const beforeKeysWindow = new Set(Object.keys(window));\n window.myopState = {\n beforeKeysWindow,\n };\n <\/script>";
2576
- const node = temp.body.firstChild;
2577
- if (node && _doc.head) {
2578
- _doc.head.insertBefore(_doc.importNode(node, true), _doc.head.firstChild);
2579
- }
2580
- HTML2Render = "<!DOCTYPE html>" + _doc.documentElement.outerHTML;
2581
- } else if ((options == null ? void 0 : options.data) !== void 0) {
2582
- const parser = new DOMParser();
2583
- const _doc = parser.parseFromString(loaderConfig.HTML, "text/html");
2584
- const myop_preview_script = _doc.getElementById("myop_preview");
2585
- if (myop_preview_script) {
2586
- myop_preview_script.remove();
2587
- HTML2Render = "<!DOCTYPE html>" + _doc.documentElement.outerHTML;
2588
- }
2589
- }
2590
- if (options == null ? void 0 : options.injectCss) {
2591
- const parser = new DOMParser();
2592
- const _doc = parser.parseFromString(HTML2Render, "text/html");
2593
- const styleTag = _doc.createElement("style");
2594
- styleTag.setAttribute("data-myop-injected", "true");
2595
- styleTag.textContent = options.injectCss.replace(/<\s*\/\s*style/gi, "<\\/style");
2596
- (_doc.head || _doc.documentElement).appendChild(styleTag);
2597
- HTML2Render = "<!DOCTYPE html>" + _doc.documentElement.outerHTML;
2598
- }
2599
- doc.writeln(HTML2Render);
2600
- const sdkScript = HAS_EMBEDDED_SDK ? "<script>".concat(EMBEDDED_SDK_CONTENT, "<\/script>") : '<script src="'.concat(window.myop.__ROOT_SDK_PUBLIC_URL__, '"><\/script>');
2601
- const initScript = HAS_EMBEDDED_SDK ? "<script>\n const __myop_init = async () => {\n const iframeModule = await MyopSDK.getIframeModule();\n const IframeSDK = iframeModule.IframeSDK;\n const sdk = new IframeSDK();\n sdk.init();\n }\n __myop_init().then();\n <\/script>" : "<script>\n window.__federation__.__public_path__ = window.__federation__.__public_path__;\n const __myop_init = async () => {\n const {IframeSDK} = (await window.myop.rootSDK.getIframeModule());\n const sdk = new IframeSDK();\n sdk.init();\n }\n __myop_init().then();\n <\/script>";
2602
- if (!isolate) {
2603
- doc.writeln("".concat(sdkScript).concat(initScript));
2604
- }
2605
- if (!isolate && (options == null ? void 0 : options.data) !== void 0) {
2606
- 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>";
2607
- const escapedData = JSON.stringify(options.data).replace(/<\/script/gi, "\\u003c/script");
2608
- 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>");
2609
- doc.writeln(ctaQueueScript + dataScript);
2610
- }
2611
- if (isolate) {
2612
- const escapedData = (options == null ? void 0 : options.data) !== void 0 ? JSON.stringify(options.data).replace(/<\/script/gi, "\\u003c/script") : "undefined";
2613
- doc.writeln(this.buildIsolationBridge(escapedData));
2614
- }
2615
- doc.close();
2616
- const comp = new MyopIframeComponent(componentDefinition, skin, container, IframeElement, options);
2617
- if (isolate) comp.enableIsolation();
2618
- comp.sizeMeta = parsedSizeMeta;
2619
- comp.initiated().then(() => {
2620
- if ((options == null ? void 0 : options.autoSize) === false) {
2621
- return;
2622
- }
2623
- let shown = false;
2624
- comp.observeAutoSize({
2625
- forceAutoSize: true,
2626
- onSizeChange: () => {
2627
- if (!shown && !(options == null ? void 0 : options.hidden)) {
2628
- shown = true;
2629
- comp.show();
2638
+ const cacheable = this._delivery.cache && !this._staticMode && !usePreview;
2639
+ const storeKey = cacheable ? deliveryStore.key(
2640
+ this.deliveryHost(),
2641
+ componentId,
2642
+ env,
2643
+ usePreview,
2644
+ this._delivery.minify ? "min" : "raw"
2645
+ ) : "";
2646
+ let held = cacheable ? await deliveryStore.get(storeKey) : null;
2647
+ if (held && deliveryStore.fresh(held)) {
2648
+ this._deliveryStats.fromStorage++;
2649
+ this.noteLocalServe(componentId, env);
2650
+ this.variants[cacheKey].resolved = held.config;
2651
+ resolve(held.config);
2652
+ return;
2653
+ }
2654
+ if (held && held.tag) {
2655
+ const checkUrl = "/consume?ids=".concat(componentId, "&env=").concat(env) + (this._delivery.minify ? "&opt=min" : "") + "&have=".concat(encodeURIComponent(held.tag));
2656
+ this._deliveryStats.revalidations++;
2657
+ try {
2658
+ const check = await this.deliveryFetch(checkUrl);
2659
+ if (check.ok) {
2660
+ const body = await check.json();
2661
+ if ((body.unchanged || []).includes(componentId)) {
2662
+ this._deliveryStats.unchanged++;
2663
+ this._deliveryStats.bytesSaved += _CloudRepository.sizeOfHeld(held);
2664
+ held.storedAt = Date.now();
2665
+ held.windowMs = deliveryStore.windowFrom(check.headers) || held.windowMs;
2666
+ await deliveryStore.put(held);
2667
+ this.variants[cacheKey].resolved = held.config;
2668
+ resolve(held.config);
2669
+ return;
2670
+ }
2671
+ const item = (body.items || [])[0];
2672
+ if (item) {
2673
+ const cfg = convertV2VariantToComponentConfig(item);
2674
+ this._deliveryStats.fromNetwork++;
2675
+ await deliveryStore.put({
2676
+ key: storeKey,
2677
+ config: cfg,
2678
+ tag: item.tag || null,
2679
+ storedAt: Date.now(),
2680
+ windowMs: deliveryStore.windowFrom(check.headers),
2681
+ host: this.deliveryHost()
2682
+ });
2683
+ this.variants[cacheKey].resolved = cfg;
2684
+ resolve(cfg);
2685
+ return;
2686
+ }
2687
+ }
2688
+ } catch (e) {
2630
2689
  }
2631
2690
  }
2632
- });
2633
- });
2634
- return comp;
2635
- }
2636
- if (loaderConfig.shadowRootMode === ShadowRootModeType.none) {
2637
- const template = document.createElement("template");
2638
- template.innerHTML = loaderConfig.HTML;
2639
- element = template.content.firstElementChild;
2640
- getInitiatedSdk().then((sdk2) => {
2641
- sdk2.connectedCallback(element, element);
2642
- });
2643
- } else {
2644
- const customElementName = toCustomElementName(componentDefinition.name + skin.id);
2645
- if (!seen2.includes(customElementName)) {
2646
- createCustomWebComponent(customElementName, loaderConfig.HTML, loaderConfig.shadowRootMode);
2647
- seen2.push(customElementName);
2691
+ const res = this._staticMode ? await fetch(url) : await this.deliveryFetch(url);
2692
+ if (!res.ok) {
2693
+ reject(new Error('Failed to load component "'.concat(componentId, '" (').concat(res.status).concat(this._staticMode ? " from ".concat(url) : "", ")")));
2694
+ return;
2695
+ }
2696
+ const json = await res.json();
2697
+ const variant = json.item;
2698
+ if (!variant) {
2699
+ reject(new Error('Component "'.concat(componentId, '" not found')));
2700
+ return;
2701
+ }
2702
+ if (!variant.consume_variant || !variant.consume_variant.length) {
2703
+ reject(new Error('Component "'.concat(componentId, '" has no implementation for environment "').concat(env, '"')));
2704
+ return;
2705
+ }
2706
+ const componentConfig = convertV2VariantToComponentConfig(variant);
2707
+ this._deliveryStats.fromNetwork++;
2708
+ if (cacheable) {
2709
+ await deliveryStore.put({
2710
+ key: storeKey,
2711
+ config: componentConfig,
2712
+ tag: variant.tag || null,
2713
+ storedAt: Date.now(),
2714
+ windowMs: deliveryStore.windowFrom(res.headers),
2715
+ host: this.deliveryHost()
2716
+ });
2717
+ }
2718
+ this.variants[cacheKey].resolved = componentConfig;
2719
+ resolve(componentConfig);
2720
+ } catch (e) {
2721
+ delete this.variants[cacheKey];
2722
+ reject(e);
2723
+ }
2648
2724
  }
2649
- element = document.createElement(customElementName);
2650
- }
2651
- if (!container.querySelector('[id^="myop-comp-"]'))
2652
- container.innerHTML = "";
2653
- element.id = domId;
2654
- if (options == null ? void 0 : options.hidden) {
2655
- element.style.opacity = "0";
2656
- element.style.position = "absolute";
2657
- element.style.height = "0";
2658
- element.style.width = "0";
2659
- element.style.pointerEvents = "none";
2660
- element.style.visibility = "hidden";
2661
- }
2662
- this.appendChild(container, element, options);
2663
- return new MyopWebComponent(componentDefinition, skin, container, element, options);
2664
- });
2665
- }
2666
- /**
2667
- * Detect if HTML is a Myop Web Component (starts with marker comment)
2668
- */
2669
- isMyopWebComponent(html) {
2670
- return html.trimStart().startsWith(MYOP_WEB_COMPONENT_MARKER);
2725
+ );
2726
+ }
2727
+ return await this.variants[cacheKey];
2671
2728
  }
2672
2729
  /**
2673
- * Resolve how this component should render, applying the host render policy.
2674
- * Precedence: options.renderPolicy (per-call) fully replaces the global
2675
- * hostSDK.setRenderPolicy — so an app can restrict globally and allow specifics.
2676
- * With no policy set, behavior is unchanged from before (marker ⇒ web component).
2730
+ * Synchronous version of fetchComponentV2 — returns the config only if
2731
+ * the fetch already completed, null otherwise.
2732
+ *
2733
+ * Uses the same `this.variants` cache that fetchComponentV2/preloadComponents
2734
+ * populate. Each cached Promise is tagged with a `.resolved` property when
2735
+ * it settles — this method just reads that property. No separate cache.
2736
+ *
2737
+ * Why this exists: React Native needs the config at render time to embed it
2738
+ * in the WebView HTML (can't await in useState). If preloadComponents ran
2739
+ * earlier and the fetch completed, this returns the result synchronously,
2740
+ * skipping the WEBVIEW_READY → fetch → inject round trip entirely.
2677
2741
  */
2678
- async resolveRenderMode(componentDefinition, skin, loaderConfig, isWebComponent, options) {
2679
- var _a, _b, _c, _d;
2680
- const requestedMode = isWebComponent ? "webComponent" : loaderConfig.isolation || (options == null ? void 0 : options.isolation) ? "isolatedIframe" : "iframe";
2681
- const policy = (_d = options == null ? void 0 : options.renderPolicy) != null ? _d : (_c = (_b = (_a = window == null ? void 0 : window.myop) == null ? void 0 : _a.hostSDK) == null ? void 0 : _b.getRenderPolicy) == null ? void 0 : _c.call(_b);
2682
- const mode = policy ? await policy({ componentDefinition, skin, requestedMode, isWebComponent }) : requestedMode;
2683
- return { mode, isolate: mode === "isolatedIframe" };
2742
+ getResolvedConfig(componentId, env, preview) {
2743
+ var _a;
2744
+ const { cacheKey } = this.resolveParams(componentId, env, preview);
2745
+ return ((_a = this.variants[cacheKey]) == null ? void 0 : _a.resolved) || null;
2684
2746
  }
2685
2747
  /**
2686
- * The in-frame bridge for isolated components. Runs inside the sandboxed
2687
- * (opaque-origin) iframe, translating the same author-facing contract to/from
2688
- * postMessage since the host has no same-origin access:
2689
- * • data-in : parent posts {type:'myop:init'} → calls window.myop_init_interface
2690
- * (plus a baked initial value for the first render)
2691
- * • data-out: window.myop_cta_handler → parent.postMessage({type:'myop:cta'})
2692
- * • resize : ResizeObserver → parent.postMessage({type:'myop:size'})
2748
+ * Resolve which (env, preview) pair — and therefore which cache key — a call
2749
+ * for `componentId` refers to.
2693
2750
  *
2694
- * TODO(dev-extension): the isolated path deliberately skips the same-origin
2695
- * dataScript, so it never posts the `MYOP_PREINJECTED_INIT` message the Myop dev
2696
- * extension listens for to log the INITIAL render (`init*`). Later init updates and
2697
- * all CTAs are still tracked (the extension wraps `comp.props`, which our isolated
2698
- * connectProps branch routes through), so only the first-render payload is missing
2699
- * from the extension's activity log for isolated components. Closing this would be a
2700
- * ~1-line `parent.postMessage({type:'MYOP_PREINJECTED_INIT', payload:{data}})` inside
2701
- * flushInitial() below — intentionally NOT done for now to avoid adding extension-only
2702
- * traffic to the component runtime. Web components have the same initial-init gap
2703
- * (initWithData is called on the element, not via comp.props) and it predates this.
2751
+ * Single source of truth for fetchComponentV2, getResolvedConfig and
2752
+ * hydrateComponent, so a hydrated config always lands on the key a later
2753
+ * fetch will look under.
2704
2754
  */
2705
- buildIsolationBridge(initialDataLiteral) {
2706
- return "<script>\n (function () {\n var __initial = ".concat(initialDataLiteral, ";\n var __pending;\n // data-out (cta)\n window.myop_cta_handler = function (action, payload) {\n try { parent.postMessage({ __myop: true, type: 'myop:cta', action: action, payload: payload }, '*'); } catch (e) {}\n };\n function callInit(data) {\n if (typeof window.myop_init_interface === 'function') { window.myop_init_interface(data); return true; }\n return false;\n }\n // data-in (init updates)\n window.addEventListener('message', function (e) {\n var d = e.data;\n if (!d || d.__myop !== true) return;\n if (d.type === 'myop:init') { if (!callInit(d.data)) __pending = d.data; }\n });\n // first render: use a pending update if one arrived, else the baked initial data\n (function flushInitial() {\n var data = (__pending !== undefined) ? __pending : __initial;\n if (data === undefined) return;\n if (callInit(data)) { __pending = undefined; __initial = undefined; }\n else requestAnimationFrame(flushInitial);\n })();\n // resize (report content size)\n function contentHeight() {\n // Measure the BODY (content box). documentElement.scrollHeight includes\n // the iframe viewport \u2014 which we're driving \u2014 so it would report the\n // current frame height, not the content, and never shrink.\n var b = document.body;\n if (b) return Math.max(b.scrollHeight, b.offsetHeight);\n return document.documentElement.scrollHeight;\n }\n function postSize() {\n try {\n parent.postMessage({\n __myop: true, type: 'myop:size',\n height: contentHeight(),\n width: document.documentElement.scrollWidth\n }, '*');\n } catch (e) {}\n }\n // Observe the BODY (reflows with content) \u2014 not documentElement, whose box is\n // the iframe viewport and would feed back into a resize loop. A MutationObserver\n // catches content swaps (e.g. innerHTML re-render) that don't change body's box.\n if (window.ResizeObserver) {\n try { var __ro = new ResizeObserver(postSize); if (document.body) __ro.observe(document.body); } catch (e) {}\n }\n if (window.MutationObserver && document.body) {\n try { new MutationObserver(postSize).observe(document.body, { childList: true, subtree: true, attributes: true, characterData: true }); } catch (e) {}\n }\n window.addEventListener('load', postSize);\n postSize();\n })();\n <\/script>");
2755
+ resolveParams(componentId, environmentIdentifier, preview) {
2756
+ const preloaded = this.preloadedComponents[componentId];
2757
+ const hasExplicitParams = environmentIdentifier !== void 0 || preview !== void 0;
2758
+ let env;
2759
+ let usePreview;
2760
+ if (!hasExplicitParams && preloaded) {
2761
+ env = preloaded.env;
2762
+ usePreview = preloaded.preview;
2763
+ } else {
2764
+ env = environmentIdentifier || this._defaultEnv;
2765
+ const hasEffectiveEnv = environmentIdentifier || this._envExplicitlySet;
2766
+ usePreview = preview === true ? true : preview === false ? false : !hasEffectiveEnv;
2767
+ }
2768
+ return { env, usePreview, cacheKey: "".concat(componentId, ":").concat(env, ":").concat(usePreview ? "preview" : "live") };
2707
2769
  }
2708
2770
  /**
2709
- * Extract the web component script content from HTML
2771
+ * Seed an already-resolved component config into the cache, without any
2772
+ * network request.
2773
+ *
2774
+ * The counterpart to {@link fetchComponentV2}: use it when the config was
2775
+ * resolved somewhere else — most often on a server, then embedded in the
2776
+ * page — so the browser can render the component with no round trip.
2777
+ *
2778
+ * After hydrating, `fetchComponentV2`, `getResolvedConfig` and `isPreloaded`
2779
+ * all answer for this component from memory.
2780
+ *
2781
+ * Pass the same `env` / `preview` the config was resolved with; the defaults
2782
+ * match what a plain `fetchComponentV2(componentId)` would have used.
2710
2783
  */
2711
- extractWebComponentScript(html) {
2712
- const parser = new DOMParser();
2713
- const doc = parser.parseFromString(html, "text/html");
2714
- const script = doc.getElementById(MYOP_WEB_COMPONENT_SCRIPT_ID);
2715
- return (script == null ? void 0 : script.textContent) || null;
2784
+ hydrateComponent(componentId, config, env, preview) {
2785
+ const { env: resolvedEnv, usePreview, cacheKey } = this.resolveParams(componentId, env, preview);
2786
+ const promise = Promise.resolve(config);
2787
+ promise.resolved = config;
2788
+ this.variants[cacheKey] = promise;
2789
+ this.preloadedComponents[componentId] = { env: resolvedEnv, preview: usePreview };
2716
2790
  }
2791
+ // ============ V1 Methods (Legacy) ============
2717
2792
  /**
2718
- * Execute the web component script on the host document
2793
+ * Fetch a v1 component from a flow
2719
2794
  */
2720
- executeWebComponentScript(scriptContent, scriptId) {
2721
- if (extractedWebComponentScripts.has(scriptId)) {
2722
- console.log("[MyopWebComponent] Script already executed for ".concat(scriptId, ", skipping"));
2723
- return;
2795
+ async fetchComponentV1(componentId, flowId) {
2796
+ if (flowId) {
2797
+ const flow = await this.fetchFlow(flowId);
2798
+ return flow.components.find((c) => c.type.id === componentId);
2799
+ } else {
2800
+ const flow = await this.fetchAutoFlow(componentId);
2801
+ return flow.components[0];
2724
2802
  }
2725
- const script = document.createElement("script");
2726
- script.textContent = scriptContent;
2727
- script.id = "myop-extracted-".concat(scriptId);
2728
- document.head.appendChild(script);
2729
- extractedWebComponentScripts.add(scriptId);
2730
- console.log("[MyopWebComponent] Executed web component script: ".concat(scriptId));
2731
2803
  }
2732
- /**
2733
- * Load a Myop Web Component (extracted and run on host document)
2734
- */
2735
- async loadExtractedWebComponent(componentDefinition, skin, container, html, options) {
2736
- const scriptContent = this.extractWebComponentScript(html);
2737
- if (!scriptContent) {
2738
- throw new Error('[MyopWebComponent] No <script id="myop-web-component"> found in HTML');
2739
- }
2740
- const scriptId = "".concat(componentDefinition.id, "-").concat(skin.id);
2741
- const previousTagName = window.__MYOP_TAG_NAME__;
2742
- this.executeWebComponentScript(scriptContent, scriptId);
2743
- const tagName = window.__MYOP_TAG_NAME__;
2744
- if (!tagName) {
2745
- throw new Error("[MyopWebComponent] Script did not set window.__MYOP_TAG_NAME__");
2746
- }
2747
- if (previousTagName && previousTagName !== tagName) {
2748
- console.warn("[MyopWebComponent] __MYOP_TAG_NAME__ changed from ".concat(previousTagName, " to ").concat(tagName));
2749
- }
2750
- const domId = "myop-comp-".concat(uuidv4());
2751
- const element = document.createElement(tagName);
2752
- element.id = domId;
2753
- if (options == null ? void 0 : options.hidden) {
2754
- element.style.opacity = "0";
2755
- element.style.position = "absolute";
2756
- element.style.height = "0";
2757
- element.style.width = "0";
2758
- element.style.pointerEvents = "none";
2759
- element.style.visibility = "hidden";
2760
- }
2761
- if (!container.querySelector('[id^="myop-comp-"]')) {
2762
- container.innerHTML = "";
2763
- }
2764
- this.appendChild(container, element, options);
2765
- const component = new MyopExtractedWebComponent(
2766
- componentDefinition,
2767
- skin,
2768
- container,
2769
- element,
2770
- tagName,
2771
- options
2772
- );
2773
- if ((options == null ? void 0 : options.data) !== void 0) {
2774
- component.initWithData(options.data);
2804
+ async fetchAutoFlow(componentId) {
2805
+ if (!this.userFlows[componentId]) {
2806
+ this.userFlows[componentId] = new Promise(
2807
+ async (resolve, reject) => {
2808
+ try {
2809
+ const res = await fetch(
2810
+ "".concat(this._baseUrl, "/flow?id=").concat(componentId, "&auto=true")
2811
+ );
2812
+ const json = await res.json();
2813
+ resolve(json.item);
2814
+ } catch (e) {
2815
+ delete this.userFlows[componentId];
2816
+ reject(e);
2817
+ }
2818
+ }
2819
+ );
2775
2820
  }
2776
- console.log("[MyopWebComponent] Loaded <".concat(tagName, "> into container"));
2777
- return component;
2821
+ return await this.userFlows[componentId];
2778
2822
  }
2779
- };
2780
-
2781
- // src/helpers/CloudRepository.ts
2782
- var convertV2VariantToComponentConfig = (config) => {
2783
- return {
2784
- instance: {
2785
- id: "auto",
2786
- componentId: config.componentId,
2787
- componentName: config.name,
2788
- skinSelector: {
2789
- type: "Dedicated",
2790
- skin: {
2791
- id: "skin_auto_v2_converted"
2792
- }
2793
- }
2794
- },
2795
- type: {
2796
- id: config.id,
2797
- name: config.name,
2798
- description: config.description,
2799
- props: [
2800
- {
2801
- id: "in_auto_v2_converted",
2802
- name: "myop_init_interface",
2803
- type: "any",
2804
- behavior: {
2805
- type: PropConfigBehaviorTypes.code
2806
- }
2807
- },
2808
- {
2809
- id: "out_auto_v2_converted",
2810
- name: "myop_cta_handler",
2811
- type: "any",
2812
- behavior: {
2813
- type: PropConfigBehaviorTypes.code
2823
+ async fetchFlow(flowId) {
2824
+ if (!this.userFlows[flowId]) {
2825
+ this.userFlows[flowId] = new Promise(
2826
+ async (resolve, reject) => {
2827
+ try {
2828
+ const res = await fetch(
2829
+ "".concat(this._baseUrl, "/flow?id=").concat(flowId, "&resolve=components")
2830
+ );
2831
+ const json = await res.json();
2832
+ resolve(json.item);
2833
+ } catch (e) {
2834
+ delete this.userFlows[flowId];
2835
+ reject(e);
2814
2836
  }
2815
2837
  }
2816
- ],
2817
- refs: [],
2818
- skins: [
2819
- {
2820
- id: "skin_auto_v2_converted",
2821
- name: "auto_v2_converted",
2822
- description: "",
2823
- //TODO : handle empty :
2824
- // @ts-ignore
2825
- loader: config.consume_variant[0].loader
2826
- }
2827
- ],
2828
- defaultSkin: 0
2829
- },
2830
- name: config.name
2831
- };
2832
- };
2833
- var GLOBAL_KEY = "__MYOP_CLOUD_REPOSITORY_MAIN__";
2834
- var getGlobalMain = () => {
2835
- if (typeof window !== "undefined") {
2836
- return window[GLOBAL_KEY];
2838
+ );
2839
+ }
2840
+ return await this.userFlows[flowId];
2837
2841
  }
2838
- if (typeof globalThis !== "undefined") {
2839
- return globalThis[GLOBAL_KEY];
2842
+ // ============ Backward Compatibility ============
2843
+ /**
2844
+ * @deprecated Use fetchComponentV2 or fetchComponentV1 explicitly
2845
+ * Defaults to v1 behavior for backward compatibility with existing code
2846
+ */
2847
+ async fetchComponent(componentId, flowId) {
2848
+ return this.fetchComponentV1(componentId, flowId);
2840
2849
  }
2841
- return void 0;
2842
2850
  };
2843
- var setGlobalMain = (instance) => {
2844
- if (typeof window !== "undefined") {
2845
- window[GLOBAL_KEY] = instance;
2846
- }
2847
- if (typeof globalThis !== "undefined") {
2848
- globalThis[GLOBAL_KEY] = instance;
2849
- }
2851
+ __publicField(_CloudRepository, "_main", null);
2852
+ /** The host every request goes to unless delivery() moved it. Never changes. */
2853
+ __publicField(_CloudRepository, "DEFAULT_HOST", "https://cloud.myop.dev");
2854
+ var CloudRepository = _CloudRepository;
2855
+
2856
+ // src/helpers/cloudRepositoryHelpers.ts
2857
+ var _cloudRepository = CloudRepository.Main;
2858
+ var DELIVERY_HOST = "https://delivery.myop.dev";
2859
+ var delivery = (opts = {}) => {
2860
+ var _a, _b;
2861
+ const wantsLane = opts.minify !== false || opts.cache === true;
2862
+ _cloudRepository.setDelivery({
2863
+ minify: (_a = opts.minify) != null ? _a : false,
2864
+ cache: (_b = opts.cache) != null ? _b : false,
2865
+ host: opts.host !== void 0 ? opts.host : wantsLane ? DELIVERY_HOST : null
2866
+ });
2867
+ return deliveryStats();
2850
2868
  };
2851
- var _CloudRepository = class _CloudRepository {
2852
- constructor(_baseUrl = "https://cloud.myop.dev") {
2853
- this._baseUrl = _baseUrl;
2854
- // v2 cache - keyed by componentId:env:preview|live
2855
- // Each Promise is tagged with a `resolved` property after settling (see fetchComponentV2).
2856
- __publicField(this, "variants", {});
2857
- // Track which components were preloaded and with what params
2858
- // Key: componentId, Value: { env, preview }
2859
- __publicField(this, "preloadedComponents", {});
2860
- // v1 cache
2861
- __publicField(this, "userFlows", {});
2862
- __publicField(this, "_defaultEnv", "production");
2863
- __publicField(this, "_envExplicitlySet", false);
2864
- __publicField(this, "_staticMode", false);
2865
- __publicField(this, "_queryParams", {});
2866
- }
2867
- static get Main() {
2868
- const globalMain = getGlobalMain();
2869
- if (globalMain) {
2870
- return globalMain;
2871
- }
2872
- if (!_CloudRepository._main) {
2873
- _CloudRepository._main = new _CloudRepository();
2874
- setGlobalMain(_CloudRepository._main);
2875
- }
2876
- return _CloudRepository._main;
2869
+ var deliveryRefresh = async () => {
2870
+ await _cloudRepository.refreshDelivery();
2871
+ return deliveryStats();
2872
+ };
2873
+ var deliveryStats = () => _cloudRepository.deliveryStats();
2874
+ var deliveryOff = () => {
2875
+ _cloudRepository.setDelivery({ minify: false, cache: false, host: null });
2876
+ return deliveryStats();
2877
+ };
2878
+
2879
+ // src/webcomponent/messageHandlers/BaseWebComponentMessageHandler.ts
2880
+ var BaseWebComponentMessageHandler = class extends BaseContextMessageHandler {
2881
+ constructor(messageToHost, context) {
2882
+ super(messageToHost);
2883
+ this.context = context;
2884
+ __publicField(this, "executor", (event) => {
2885
+ if (event.destination) {
2886
+ const destination = event.destination;
2887
+ if (this.context[destination].context) {
2888
+ const _messageToHost = (message, replayTo) => {
2889
+ this.messageToHost(__spreadProps(__spreadValues({}, message), {
2890
+ source: event.destination ? event.destination : event.source,
2891
+ destination: event.source ? event.source : event.destination
2892
+ }), replayTo);
2893
+ };
2894
+ return this.innerExecutor(event, this.context[destination].context, _messageToHost);
2895
+ } else {
2896
+ debugger;
2897
+ }
2898
+ } else {
2899
+ debugger;
2900
+ }
2901
+ });
2877
2902
  }
2878
- /**
2879
- * Enable static mode for self-hosted component registries.
2880
- * In static mode, URLs are constructed as file paths instead of query params:
2881
- * ${baseUrl}/components/${componentId}/${env}.json
2882
- */
2883
- enableStaticMode() {
2884
- this._staticMode = true;
2903
+ };
2904
+
2905
+ // src/webcomponent/messageHandlers/bindOnClickHandler.ts
2906
+ var BindOnClickHandler2 = class extends BaseWebComponentMessageHandler {
2907
+ constructor() {
2908
+ super(...arguments);
2909
+ __publicField(this, "messageType", MyopMessages.BindClickMessage);
2910
+ __publicField(this, "innerExecutor", (event, shadowRootRef, messageToHost) => {
2911
+ const items = getElementsByRef(event.ref, shadowRootRef.shadowRoot);
2912
+ const cleanupTasks = [];
2913
+ items.forEach((item) => {
2914
+ const handler = () => {
2915
+ messageToHost(new ClickReplayMessage(event.handlerUniqueId));
2916
+ };
2917
+ item.addEventListener("click", handler);
2918
+ cleanupTasks.push(() => {
2919
+ item.removeEventListener("click", handler);
2920
+ });
2921
+ });
2922
+ return () => {
2923
+ cleanupTasks.forEach((task) => task());
2924
+ };
2925
+ });
2885
2926
  }
2886
- /**
2887
- * Set query parameters to append to all fetch URLs (e.g. for CDN cache busting).
2888
- * Example: `setQueryParams({ lastupdate: '11112026' })`
2889
- */
2890
- setQueryParams(params) {
2891
- this._queryParams = params;
2927
+ };
2928
+
2929
+ // src/webcomponent/messageHandlers/changeTextHandler.ts
2930
+ var ChangeTextHandler2 = class extends BaseWebComponentMessageHandler {
2931
+ constructor() {
2932
+ super(...arguments);
2933
+ __publicField(this, "messageType", MyopMessages.ChangeTextMessage);
2934
+ __publicField(this, "innerExecutor", (event, shadowRootRef) => {
2935
+ const items = getElementsByRef(event.ref, shadowRootRef.shadowRoot);
2936
+ items.forEach((item) => {
2937
+ item.textContent = event.content;
2938
+ });
2939
+ });
2892
2940
  }
2893
- /**
2894
- * Set the default environment for this CloudRepository instance
2895
- */
2896
- setEnvironment(env) {
2897
- this._defaultEnv = env;
2898
- this._envExplicitlySet = true;
2941
+ };
2942
+
2943
+ // src/webcomponent/shadowRootRef.ts
2944
+ var ShadowRootRef = class extends RootRef {
2945
+ constructor(container, shadowRoot) {
2946
+ super(container, shadowRoot);
2947
+ this.container = container;
2948
+ this.shadowRoot = shadowRoot;
2899
2949
  }
2900
- /**
2901
- * Get the current default environment
2902
- */
2903
- getDefaultEnvironment() {
2904
- return this._defaultEnv;
2950
+ };
2951
+
2952
+ // src/webcomponent/messageHandlers/DetectMyopRefsMessageHandler.ts
2953
+ var DetectMyopRefsMessageHandler2 = class extends BaseWebComponentMessageHandler {
2954
+ constructor() {
2955
+ super(...arguments);
2956
+ __publicField(this, "innerExecutor", (event, shadowRootRef) => {
2957
+ const allMyopItems = shadowRootRef.shadowRoot.querySelectorAll("[".concat(myopAttributes.id, "]"));
2958
+ let myopRefs = [];
2959
+ allMyopItems.forEach((item) => {
2960
+ const myopId = item.getAttribute(myopAttributes.id);
2961
+ if (myopId) {
2962
+ myopRefs.push(new MyopElementRef(myopId, item.tagName, item.getBoundingClientRect(), item.offsetTop, item.offsetLeft));
2963
+ }
2964
+ });
2965
+ this.messageToHost(new DetectedMyopRefsMessage(event.handlerUniqueId, myopRefs));
2966
+ });
2967
+ __publicField(this, "messageType", MyopMessages.DetectMyopRefsMessage);
2905
2968
  }
2906
- // ============ V2 Methods (Default) ============
2907
- /**
2908
- * Check if a component is already cached/preloaded (v2)
2909
- * If env and preview are not provided, checks if ANY version is preloaded
2910
- */
2911
- isPreloaded(componentId, env, preview) {
2912
- if (env !== void 0 || preview !== void 0) {
2913
- const cacheKey = "".concat(componentId, ":").concat(env || this._defaultEnv, ":").concat(preview ? "preview" : "live");
2914
- return cacheKey in this.variants;
2915
- }
2916
- return componentId in this.preloadedComponents;
2969
+ };
2970
+
2971
+ // src/webcomponent/messageHandlers/setAttributeHandler.ts
2972
+ var setAttributeHandler2 = class extends BaseWebComponentMessageHandler {
2973
+ constructor() {
2974
+ super(...arguments);
2975
+ __publicField(this, "messageType", MyopMessages.SetAttributeMessage);
2976
+ __publicField(this, "innerExecutor", (event, shadowRootRef) => {
2977
+ const items = getElementsByRef(event.ref, shadowRootRef.shadowRoot);
2978
+ items.forEach((item) => {
2979
+ item.setAttribute(event.name, event.value);
2980
+ });
2981
+ });
2917
2982
  }
2918
- /**
2919
- * Get the preloaded params for a component (if preloaded)
2920
- * Returns undefined if not preloaded
2921
- */
2922
- getPreloadedParams(componentId) {
2923
- return this.preloadedComponents[componentId];
2983
+ };
2984
+
2985
+ // src/webcomponent/messageHandlers/addEventListenerHandler.ts
2986
+ var AddEventListenerHandler2 = class extends BaseWebComponentMessageHandler {
2987
+ constructor() {
2988
+ super(...arguments);
2989
+ __publicField(this, "messageType", MyopMessages.AddEventListenerMessage);
2990
+ __publicField(this, "innerExecutor", (event, shadowRootRef, messageToHost) => {
2991
+ const items = getElementsByRef(event.ref, shadowRootRef.shadowRoot);
2992
+ const cleanupTasks = [];
2993
+ items.forEach((item) => {
2994
+ const handler = (e) => {
2995
+ messageToHost(new EventListenerCallbackMessage(event.handlerUniqueId, makeSerializable(e, event.serializableSkeleton)));
2996
+ };
2997
+ item.addEventListener(event.type, handler);
2998
+ cleanupTasks.push(() => {
2999
+ item.removeEventListener(event.type, handler);
3000
+ });
3001
+ });
3002
+ return () => {
3003
+ cleanupTasks.forEach((task) => task());
3004
+ };
3005
+ });
2924
3006
  }
2925
- /**
2926
- * Resolve a component config: fetch it once, then serve it from this
2927
- * repository's cache.
2928
- *
2929
- * This is the name to use. `fetchComponentV2` is the same method under its
2930
- * versioned name and stays for backward compatibility — note that the
2931
- * unversioned `fetchComponent` is a *deprecated v1* API and is NOT this.
2932
- *
2933
- * Completes the trio with {@link getResolvedConfig} (synchronous read of an
2934
- * already-resolved config) and {@link hydrateComponent} (seed one you
2935
- * already hold).
2936
- *
2937
- * Prefer the module-level `preloadComponents([id])` in app code; reach for
2938
- * this when you need an isolated repository instance whose cache lifetime
2939
- * you control — a server with its own TTL, for example.
2940
- */
2941
- async resolveComponent(componentId, env, preview) {
2942
- return this.fetchComponentV2(componentId, env, preview);
3007
+ };
3008
+
3009
+ // src/webcomponent/messageHandlers/CreateRefComponentMessageHandler.ts
3010
+ var CreateRefComponentMessageHandler = class extends BaseWebComponentMessageHandler {
3011
+ constructor() {
3012
+ super(...arguments);
3013
+ __publicField(this, "messageType", MyopMessages.CreateRefComponentMessage);
3014
+ __publicField(this, "innerExecutor", (event, shadowRootRef, messageToHost) => {
3015
+ const createRef = async () => {
3016
+ const allItems = shadowRootRef.shadowRoot.querySelectorAll("[".concat(myopAttributes.id, "='").concat(event.refConfig.selector, "']"));
3017
+ if (allItems.length === 1) {
3018
+ const hostSDK2 = HostSDK.Instance();
3019
+ const refComponent = hostSDK2.components.find((c) => c.id === event.options.id);
3020
+ if (refComponent) {
3021
+ refComponent.dispose();
3022
+ } else {
3023
+ }
3024
+ const index = hostSDK2.components.indexOf(refComponent);
3025
+ if (index !== -1) {
3026
+ hostSDK2.components.splice(index, 1);
3027
+ } else {
3028
+ }
3029
+ const implementationComponent = await hostSDK2.loadComponent(
3030
+ event.nestedComponentConfig,
3031
+ allItems.item(0),
3032
+ event.options
3033
+ );
3034
+ const nestedRefConfig = [];
3035
+ Object.keys(implementationComponent.refs).forEach((refName) => {
3036
+ let ref = implementationComponent.refs[refName];
3037
+ ref = ref.__nonProxied || ref;
3038
+ if (ref.componentDefinition) {
3039
+ nestedRefConfig.push({
3040
+ refName,
3041
+ componentDefinitionId: ref.componentDefinition.id
3042
+ });
3043
+ }
3044
+ });
3045
+ messageToHost(new RefComponentCreatedMessage(event.handlerUniqueId, nestedRefConfig));
3046
+ } else {
3047
+ console.error("CreateRefComponentMessageHandler - DOM element not found");
3048
+ this.messageToHost(new RefComponentCreatedMessage(event.handlerUniqueId, [], true));
3049
+ debugger;
3050
+ }
3051
+ };
3052
+ createRef().then();
3053
+ });
2943
3054
  }
2944
- /**
2945
- * Fetch a v2 component config
2946
- * If env/preview not specified and component was preloaded, uses preloaded params
2947
- *
2948
- * @see resolveComponent — the unversioned name for this method
2949
- */
2950
- async fetchComponentV2(componentId, environmentIdentifier, preview) {
2951
- const { env, usePreview, cacheKey } = this.resolveParams(componentId, environmentIdentifier, preview);
2952
- console.log("[CloudRepository] fetchComponentV2", {
2953
- componentId: componentId.slice(0, 8) + "...",
2954
- env,
2955
- usePreview,
2956
- cacheKey,
2957
- cached: cacheKey in this.variants
3055
+ };
3056
+
3057
+ // src/webcomponent/messageHandlers/executeScriptHandler.ts
3058
+ var ExecuteScriptHandler2 = class extends BaseWebComponentMessageHandler {
3059
+ constructor() {
3060
+ super(...arguments);
3061
+ __publicField(this, "messageType", MyopMessages.ExecuteScriptMessage);
3062
+ __publicField(this, "innerExecutor", (event, shadowRootRef, messageToHost) => {
3063
+ const replier = (data) => {
3064
+ messageToHost(new ExecuteScriptReplyMessage(event.handlerUniqueId, data));
3065
+ };
3066
+ ExecuteScriptHandlerExecutor(event, replier, shadowRootRef);
2958
3067
  });
2959
- if (!this.variants[cacheKey]) {
2960
- if (!this.preloadedComponents[componentId]) {
2961
- this.preloadedComponents[componentId] = { env, preview: usePreview };
2962
- }
2963
- this.variants[cacheKey] = new Promise(
2964
- async (resolve, reject) => {
2965
- try {
2966
- let url;
2967
- if (this._staticMode) {
2968
- const file = usePreview ? "preview" : env;
2969
- url = "".concat(this._baseUrl, "/components/").concat(componentId, "/").concat(file, ".json");
2970
- const qs = new URLSearchParams(this._queryParams).toString();
2971
- if (qs) url += "?".concat(qs);
3068
+ }
3069
+ };
3070
+
3071
+ // src/webcomponent/messageHandlers/GetElementValueHandler.ts
3072
+ var getInputValue = (element) => {
3073
+ if (!element) return null;
3074
+ if (element.tagName === "FORM") {
3075
+ const formData = new FormData(element);
3076
+ const dataObject = {
3077
+ formData: {},
3078
+ unmappedData: []
3079
+ };
3080
+ const elements = element.querySelectorAll("input, select, textarea");
3081
+ let autoNameIndexer = -1;
3082
+ elements.forEach((element2) => {
3083
+ if (typeof element2.value === "string") {
3084
+ const myopId = element2.getAttribute(myopAttributes.id);
3085
+ let key = element2.getAttribute("name");
3086
+ const nodeData = {
3087
+ id: element2.id,
3088
+ name: key,
3089
+ value: getInputValue(element2),
3090
+ required: element2.required,
3091
+ validity: makeSerializable(element2.validity, true),
3092
+ myopId
3093
+ };
3094
+ if (!key) {
3095
+ dataObject.unmappedData.push(nodeData);
3096
+ } else {
3097
+ if (dataObject.formData[key]) {
3098
+ if (dataObject.formData[key].value) {
3099
+ dataObject.unmappedData.push(nodeData);
2972
3100
  } else {
2973
- url = "".concat(this._baseUrl, "/consume?id=").concat(componentId, "&env=").concat(env);
2974
- if (usePreview) {
2975
- url += "&preview=true";
2976
- }
2977
- }
2978
- const res = await fetch(url);
2979
- if (!res.ok) {
2980
- reject(new Error('Failed to load component "'.concat(componentId, '" (').concat(res.status).concat(this._staticMode ? " from ".concat(url) : "", ")")));
2981
- return;
2982
- }
2983
- const json = await res.json();
2984
- const variant = json.item;
2985
- if (!variant) {
2986
- reject(new Error('Component "'.concat(componentId, '" not found')));
2987
- return;
3101
+ dataObject.unmappedData.push(dataObject.formData[key]);
3102
+ dataObject.formData[key] = nodeData;
2988
3103
  }
2989
- if (!variant.consume_variant || !variant.consume_variant.length) {
2990
- reject(new Error('Component "'.concat(componentId, '" has no implementation for environment "').concat(env, '"')));
2991
- return;
3104
+ } else {
3105
+ dataObject.formData[key] = nodeData;
3106
+ }
3107
+ }
3108
+ }
3109
+ });
3110
+ return dataObject;
3111
+ }
3112
+ if (element.type === "checkbox" || element.type === "radio") {
3113
+ return element.checked ? element.value : null;
3114
+ }
3115
+ if (element.tagName === "INPUT" || element.tagName === "TEXTAREA") {
3116
+ return element.value;
3117
+ }
3118
+ if (element.tagName === "SELECT") {
3119
+ return element.value;
3120
+ }
3121
+ if (element.isContentEditable) {
3122
+ return element.innerText || element.textContent;
3123
+ }
3124
+ return null;
3125
+ };
3126
+ var GetElementValueHandler = class extends BaseWebComponentMessageHandler {
3127
+ constructor() {
3128
+ super(...arguments);
3129
+ __publicField(this, "messageType", MyopMessages.GetElementValueMessage);
3130
+ __publicField(this, "innerExecutor", (event, shadowRootRef, messageToHost) => {
3131
+ const items = getElementsByRef(event.ref, shadowRootRef.shadowRoot);
3132
+ items.forEach((item) => {
3133
+ messageToHost(new ElementValueReplayMessage(getInputValue(item)), event);
3134
+ });
3135
+ });
3136
+ }
3137
+ };
3138
+
3139
+ // src/webcomponent/messageHandlers/setInnerHtmlHandler.ts
3140
+ var SetInnerHtmlHandler = class extends BaseWebComponentMessageHandler {
3141
+ constructor() {
3142
+ super(...arguments);
3143
+ __publicField(this, "messageType", MyopMessages.SetInnerHtml);
3144
+ __publicField(this, "innerExecutor", (event, shadowRootRef) => {
3145
+ const items = getElementsByRef(event.ref, shadowRootRef.shadowRoot);
3146
+ items.forEach((item) => {
3147
+ item.innerHTML = event.content;
3148
+ });
3149
+ });
3150
+ }
3151
+ };
3152
+
3153
+ // src/webcomponent/WebComponentSDK.ts
3154
+ var WebComponentSDK = class extends BaseMyopSDK {
3155
+ constructor() {
3156
+ super(...arguments);
3157
+ __publicField(this, "alwaysPassEnvelopesToHost", true);
3158
+ //when new web component created.
3159
+ __publicField(this, "connectedCallback", (container, shadow) => {
3160
+ container.addEventListener(myopWebcomponentConsts.webcomponent_message_key, (e) => {
3161
+ const eventContent = e.data ? e.data : e.detail;
3162
+ if (!eventContent || !eventContent.myop) {
3163
+ debugger;
3164
+ throw new Error("recvied webcomponent_message with wrong type");
3165
+ } else {
3166
+ const message = eventContent;
3167
+ this.handleIncomeMessages(e);
3168
+ if (message.messageType === MyopMessages.InitMessage) {
3169
+ const newComponentId = message.content.id;
3170
+ this.components[newComponentId].context = new ShadowRootRef(container, shadow);
3171
+ }
3172
+ }
3173
+ });
3174
+ setTimeout(() => {
3175
+ this.messageToHost(new InitRequest());
3176
+ }, 1);
3177
+ });
3178
+ __publicField(this, "messageToHost", (message, replayTo) => {
3179
+ message.direction = MessageDirection.up;
3180
+ if (!message.replayToHandler && (replayTo == null ? void 0 : replayTo.handlerUniqueId))
3181
+ message.replayToHandler = replayTo.handlerUniqueId;
3182
+ window.myop.hostSDK.send(message);
3183
+ });
3184
+ __publicField(this, "supportedHandlers", [
3185
+ new ChangeTextHandler2(this.messageToHost, this.components),
3186
+ new SetInnerHtmlHandler(this.messageToHost, this.components),
3187
+ new BindOnClickHandler2(this.messageToHost, this.components),
3188
+ new DetectMyopRefsMessageHandler2(this.messageToHost, this.components),
3189
+ new setAttributeHandler2(this.messageToHost, this.components),
3190
+ new AddEventListenerHandler2(this.messageToHost, this.components),
3191
+ new ExecuteScriptHandler2(this.messageToHost, this.components),
3192
+ new GetElementValueHandler(this.messageToHost, this.components),
3193
+ //
3194
+ new CreateRefComponentMessageHandler(this.messageToHost, this.components)
3195
+ ]);
3196
+ __publicField(this, "init", () => {
3197
+ });
3198
+ }
3199
+ };
3200
+ defineDynamicImport("WebComponentSDK", WebComponentSDK);
3201
+
3202
+ // src/host/embeddedSDK.ts
3203
+ var EMBEDDED_SDK_CONTENT = "";
3204
+ var HAS_EMBEDDED_SDK = false;
3205
+
3206
+ // src/host/loaders/HTMLComponentLoader.ts
3207
+ var MYOP_WEB_COMPONENT_MARKER = "<!-- myop-web-component -->";
3208
+ var MYOP_WEB_COMPONENT_SCRIPT_ID = "myop-web-component";
3209
+ var extractedWebComponentScripts = /* @__PURE__ */ new Map();
3210
+ var seen2 = [];
3211
+ var sdk;
3212
+ var getInitiatedSdk = async () => {
3213
+ if (!sdk) {
3214
+ sdk = new WebComponentSDK();
3215
+ sdk.init();
3216
+ }
3217
+ return sdk;
3218
+ };
3219
+ function createHTMLElementConstructor(htmlString, ShadowRootMode) {
3220
+ return class extends HTMLElement {
3221
+ connectedCallback() {
3222
+ const mode = ShadowRootMode === ShadowRootModeType.open ? "open" : "closed";
3223
+ const shadow = this.attachShadow({ mode });
3224
+ getInitiatedSdk().then((sdk2) => {
3225
+ sdk2.connectedCallback(this, shadow);
3226
+ shadow.innerHTML = htmlString;
3227
+ });
3228
+ }
3229
+ };
3230
+ }
3231
+ var createCustomWebComponent = (componentName, html, ShadowRootMode) => {
3232
+ customElements.define(componentName, createHTMLElementConstructor(html, ShadowRootMode));
3233
+ console.log("define('".concat(componentName, "') was called, web component ready to use"));
3234
+ };
3235
+ var toWebComponentName = (str) => {
3236
+ return str.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/(\d+)/g, "-$1").toLowerCase();
3237
+ };
3238
+ var toCustomElementName = (name) => {
3239
+ name = toWebComponentName(name);
3240
+ return name.replace(/_/g, "-");
3241
+ };
3242
+ var HTMLComponentLoader = class extends BaseMyopLoader {
3243
+ constructor() {
3244
+ super(...arguments);
3245
+ __publicField(this, "type", SkinLoaders.HTMLLoader);
3246
+ __publicField(this, "load", async (componentDefinition, skin, container, options) => {
3247
+ const loaderConfig = skin.loader;
3248
+ const isWC = this.isMyopWebComponent(loaderConfig.HTML);
3249
+ const { mode, isolate } = await this.resolveRenderMode(componentDefinition, skin, loaderConfig, isWC, options);
3250
+ if (mode === false) {
3251
+ throw new Error('[Myop] Rendering blocked by render policy for component "'.concat(componentDefinition.id, '"'));
3252
+ }
3253
+ if (isWC) {
3254
+ if (mode !== "webComponent") {
3255
+ throw new Error('[Myop] Web-component rendering denied by render policy for component "'.concat(componentDefinition.id, '"'));
3256
+ }
3257
+ return this.loadExtractedWebComponent(componentDefinition, skin, container, loaderConfig.HTML, options);
3258
+ }
3259
+ let domId = "myop-comp-".concat(uuidv4());
3260
+ let element;
3261
+ if (loaderConfig.shadowRootMode === ShadowRootModeType.localFrame) {
3262
+ const uniqId = uuidv4();
3263
+ const domId2 = "myop-comp-".concat(uniqId);
3264
+ let IframeElement = document.createElement("iframe");
3265
+ IframeElement.id = domId2;
3266
+ if (isolate) {
3267
+ IframeElement.setAttribute("sandbox", "allow-scripts allow-forms allow-popups");
3268
+ }
3269
+ IframeElement.style.cssText = "\n display: block;\n padding: 0;\n margin: 0;\n position: absolute;\n inset: 0;\n overflow: hidden;\n border: none;\n opacity: ".concat((options == null ? void 0 : options.hidden) ? "0" : "1", ";\n width: 100%;\n height: 100%;\n pointer-events: ").concat((options == null ? void 0 : options.hidden) ? "none" : "all", ";\n ");
3270
+ if (!container.querySelector('[id^="myop-comp-"]'))
3271
+ container.innerHTML = "";
3272
+ IframeElement = this.appendChild(container, IframeElement, options);
3273
+ const __srcParts = [];
3274
+ const doc = isolate ? {
3275
+ open() {
3276
+ },
3277
+ writeln(s) {
3278
+ __srcParts.push(s);
3279
+ },
3280
+ close() {
3281
+ IframeElement.srcdoc = __srcParts.join("\n");
3282
+ }
3283
+ } : (() => {
3284
+ const d = IframeElement.contentDocument || IframeElement.contentWindow.document;
3285
+ d.open();
3286
+ return d;
3287
+ })();
3288
+ let parsedSizeMeta = null;
3289
+ {
3290
+ const _preParser = new DOMParser();
3291
+ const _preDoc = _preParser.parseFromString(loaderConfig.HTML, "text/html");
3292
+ const sizeMeta = _preDoc.querySelector('meta[name="myop:size"]');
3293
+ if (sizeMeta) {
3294
+ try {
3295
+ parsedSizeMeta = JSON.parse(sizeMeta.getAttribute("content") || "{}");
3296
+ } catch (e) {
2992
3297
  }
2993
- const componentConfig = convertV2VariantToComponentConfig(variant);
2994
- this.variants[cacheKey].resolved = componentConfig;
2995
- resolve(componentConfig);
2996
- } catch (e) {
2997
- delete this.variants[cacheKey];
2998
- reject(e);
2999
3298
  }
3000
3299
  }
3001
- );
3002
- }
3003
- return await this.variants[cacheKey];
3300
+ let HTML2Render = loaderConfig.HTML;
3301
+ if (options == null ? void 0 : options._inspection) {
3302
+ const parser = new DOMParser();
3303
+ const _doc = parser.parseFromString(loaderConfig.HTML, "text/html");
3304
+ const myop_preview_script = _doc.getElementById("myop_preview");
3305
+ myop_preview_script == null ? void 0 : myop_preview_script.removeAttribute("type");
3306
+ const temp = _doc.implementation.createHTMLDocument();
3307
+ temp.body.innerHTML = "<script>\n const beforeKeysWindow = new Set(Object.keys(window));\n window.myopState = {\n beforeKeysWindow,\n };\n <\/script>";
3308
+ const node = temp.body.firstChild;
3309
+ if (node && _doc.head) {
3310
+ _doc.head.insertBefore(_doc.importNode(node, true), _doc.head.firstChild);
3311
+ }
3312
+ HTML2Render = "<!DOCTYPE html>" + _doc.documentElement.outerHTML;
3313
+ } else if ((options == null ? void 0 : options.data) !== void 0) {
3314
+ const parser = new DOMParser();
3315
+ const _doc = parser.parseFromString(loaderConfig.HTML, "text/html");
3316
+ const myop_preview_script = _doc.getElementById("myop_preview");
3317
+ if (myop_preview_script) {
3318
+ myop_preview_script.remove();
3319
+ HTML2Render = "<!DOCTYPE html>" + _doc.documentElement.outerHTML;
3320
+ }
3321
+ }
3322
+ if (options == null ? void 0 : options.injectCss) {
3323
+ const parser = new DOMParser();
3324
+ const _doc = parser.parseFromString(HTML2Render, "text/html");
3325
+ const styleTag = _doc.createElement("style");
3326
+ styleTag.setAttribute("data-myop-injected", "true");
3327
+ styleTag.textContent = options.injectCss.replace(/<\s*\/\s*style/gi, "<\\/style");
3328
+ (_doc.head || _doc.documentElement).appendChild(styleTag);
3329
+ HTML2Render = "<!DOCTYPE html>" + _doc.documentElement.outerHTML;
3330
+ }
3331
+ doc.writeln(HTML2Render);
3332
+ const sdkScript = HAS_EMBEDDED_SDK ? "<script>".concat(EMBEDDED_SDK_CONTENT, "<\/script>") : '<script src="'.concat(window.myop.__ROOT_SDK_PUBLIC_URL__, '"><\/script>');
3333
+ const initScript = HAS_EMBEDDED_SDK ? "<script>\n const __myop_init = async () => {\n const iframeModule = await MyopSDK.getIframeModule();\n const IframeSDK = iframeModule.IframeSDK;\n const sdk = new IframeSDK();\n sdk.init();\n }\n __myop_init().then();\n <\/script>" : "<script>\n window.__federation__.__public_path__ = window.__federation__.__public_path__;\n const __myop_init = async () => {\n const {IframeSDK} = (await window.myop.rootSDK.getIframeModule());\n const sdk = new IframeSDK();\n sdk.init();\n }\n __myop_init().then();\n <\/script>";
3334
+ if (!isolate) {
3335
+ doc.writeln("".concat(sdkScript).concat(initScript));
3336
+ }
3337
+ if (!isolate && (options == null ? void 0 : options.data) !== void 0) {
3338
+ 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>";
3339
+ const escapedData = JSON.stringify(options.data).replace(/<\/script/gi, "\\u003c/script");
3340
+ 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>");
3341
+ doc.writeln(ctaQueueScript + dataScript);
3342
+ }
3343
+ if (isolate) {
3344
+ const escapedData = (options == null ? void 0 : options.data) !== void 0 ? JSON.stringify(options.data).replace(/<\/script/gi, "\\u003c/script") : "undefined";
3345
+ doc.writeln(this.buildIsolationBridge(escapedData));
3346
+ }
3347
+ doc.close();
3348
+ const comp = new MyopIframeComponent(componentDefinition, skin, container, IframeElement, options);
3349
+ if (isolate) comp.enableIsolation();
3350
+ comp.sizeMeta = parsedSizeMeta;
3351
+ comp.initiated().then(() => {
3352
+ if ((options == null ? void 0 : options.autoSize) === false) {
3353
+ return;
3354
+ }
3355
+ let shown = false;
3356
+ comp.observeAutoSize({
3357
+ forceAutoSize: true,
3358
+ onSizeChange: () => {
3359
+ if (!shown && !(options == null ? void 0 : options.hidden)) {
3360
+ shown = true;
3361
+ comp.show();
3362
+ }
3363
+ }
3364
+ });
3365
+ });
3366
+ return comp;
3367
+ }
3368
+ if (loaderConfig.shadowRootMode === ShadowRootModeType.none) {
3369
+ const template = document.createElement("template");
3370
+ template.innerHTML = loaderConfig.HTML;
3371
+ element = template.content.firstElementChild;
3372
+ getInitiatedSdk().then((sdk2) => {
3373
+ sdk2.connectedCallback(element, element);
3374
+ });
3375
+ } else {
3376
+ const customElementName = toCustomElementName(componentDefinition.name + skin.id);
3377
+ if (!seen2.includes(customElementName)) {
3378
+ createCustomWebComponent(customElementName, loaderConfig.HTML, loaderConfig.shadowRootMode);
3379
+ seen2.push(customElementName);
3380
+ }
3381
+ element = document.createElement(customElementName);
3382
+ }
3383
+ if (!container.querySelector('[id^="myop-comp-"]'))
3384
+ container.innerHTML = "";
3385
+ element.id = domId;
3386
+ if (options == null ? void 0 : options.hidden) {
3387
+ element.style.opacity = "0";
3388
+ element.style.position = "absolute";
3389
+ element.style.height = "0";
3390
+ element.style.width = "0";
3391
+ element.style.pointerEvents = "none";
3392
+ element.style.visibility = "hidden";
3393
+ }
3394
+ this.appendChild(container, element, options);
3395
+ return new MyopWebComponent(componentDefinition, skin, container, element, options);
3396
+ });
3004
3397
  }
3005
3398
  /**
3006
- * Synchronous version of fetchComponentV2 — returns the config only if
3007
- * the fetch already completed, null otherwise.
3008
- *
3009
- * Uses the same `this.variants` cache that fetchComponentV2/preloadComponents
3010
- * populate. Each cached Promise is tagged with a `.resolved` property when
3011
- * it settles — this method just reads that property. No separate cache.
3012
- *
3013
- * Why this exists: React Native needs the config at render time to embed it
3014
- * in the WebView HTML (can't await in useState). If preloadComponents ran
3015
- * earlier and the fetch completed, this returns the result synchronously,
3016
- * skipping the WEBVIEW_READY → fetch → inject round trip entirely.
3399
+ * Detect if HTML is a Myop Web Component (starts with marker comment)
3017
3400
  */
3018
- getResolvedConfig(componentId, env, preview) {
3019
- var _a;
3020
- const { cacheKey } = this.resolveParams(componentId, env, preview);
3021
- return ((_a = this.variants[cacheKey]) == null ? void 0 : _a.resolved) || null;
3401
+ isMyopWebComponent(html) {
3402
+ return html.trimStart().startsWith(MYOP_WEB_COMPONENT_MARKER);
3022
3403
  }
3023
3404
  /**
3024
- * Resolve which (env, preview) pair — and therefore which cache key — a call
3025
- * for `componentId` refers to.
3026
- *
3027
- * Single source of truth for fetchComponentV2, getResolvedConfig and
3028
- * hydrateComponent, so a hydrated config always lands on the key a later
3029
- * fetch will look under.
3405
+ * Resolve how this component should render, applying the host render policy.
3406
+ * Precedence: options.renderPolicy (per-call) fully replaces the global
3407
+ * hostSDK.setRenderPolicy — so an app can restrict globally and allow specifics.
3408
+ * With no policy set, behavior is unchanged from before (marker ⇒ web component).
3030
3409
  */
3031
- resolveParams(componentId, environmentIdentifier, preview) {
3032
- const preloaded = this.preloadedComponents[componentId];
3033
- const hasExplicitParams = environmentIdentifier !== void 0 || preview !== void 0;
3034
- let env;
3035
- let usePreview;
3036
- if (!hasExplicitParams && preloaded) {
3037
- env = preloaded.env;
3038
- usePreview = preloaded.preview;
3039
- } else {
3040
- env = environmentIdentifier || this._defaultEnv;
3041
- const hasEffectiveEnv = environmentIdentifier || this._envExplicitlySet;
3042
- usePreview = preview === true ? true : preview === false ? false : !hasEffectiveEnv;
3043
- }
3044
- return { env, usePreview, cacheKey: "".concat(componentId, ":").concat(env, ":").concat(usePreview ? "preview" : "live") };
3410
+ async resolveRenderMode(componentDefinition, skin, loaderConfig, isWebComponent, options) {
3411
+ var _a, _b, _c, _d;
3412
+ const requestedMode = isWebComponent ? "webComponent" : loaderConfig.isolation || (options == null ? void 0 : options.isolation) ? "isolatedIframe" : "iframe";
3413
+ const policy = (_d = options == null ? void 0 : options.renderPolicy) != null ? _d : (_c = (_b = (_a = window == null ? void 0 : window.myop) == null ? void 0 : _a.hostSDK) == null ? void 0 : _b.getRenderPolicy) == null ? void 0 : _c.call(_b);
3414
+ const mode = policy ? await policy({ componentDefinition, skin, requestedMode, isWebComponent }) : requestedMode;
3415
+ return { mode, isolate: mode === "isolatedIframe" };
3045
3416
  }
3046
3417
  /**
3047
- * Seed an already-resolved component config into the cache, without any
3048
- * network request.
3049
- *
3050
- * The counterpart to {@link fetchComponentV2}: use it when the config was
3051
- * resolved somewhere else — most often on a server, then embedded in the
3052
- * page — so the browser can render the component with no round trip.
3053
- *
3054
- * After hydrating, `fetchComponentV2`, `getResolvedConfig` and `isPreloaded`
3055
- * all answer for this component from memory.
3418
+ * The in-frame bridge for isolated components. Runs inside the sandboxed
3419
+ * (opaque-origin) iframe, translating the same author-facing contract to/from
3420
+ * postMessage since the host has no same-origin access:
3421
+ * • data-in : parent posts {type:'myop:init'} → calls window.myop_init_interface
3422
+ * (plus a baked initial value for the first render)
3423
+ * • data-out: window.myop_cta_handler → parent.postMessage({type:'myop:cta'})
3424
+ * • resize : ResizeObserver → parent.postMessage({type:'myop:size'})
3056
3425
  *
3057
- * Pass the same `env` / `preview` the config was resolved with; the defaults
3058
- * match what a plain `fetchComponentV2(componentId)` would have used.
3426
+ * TODO(dev-extension): the isolated path deliberately skips the same-origin
3427
+ * dataScript, so it never posts the `MYOP_PREINJECTED_INIT` message the Myop dev
3428
+ * extension listens for to log the INITIAL render (`init*`). Later init updates and
3429
+ * all CTAs are still tracked (the extension wraps `comp.props`, which our isolated
3430
+ * connectProps branch routes through), so only the first-render payload is missing
3431
+ * from the extension's activity log for isolated components. Closing this would be a
3432
+ * ~1-line `parent.postMessage({type:'MYOP_PREINJECTED_INIT', payload:{data}})` inside
3433
+ * flushInitial() below — intentionally NOT done for now to avoid adding extension-only
3434
+ * traffic to the component runtime. Web components have the same initial-init gap
3435
+ * (initWithData is called on the element, not via comp.props) and it predates this.
3059
3436
  */
3060
- hydrateComponent(componentId, config, env, preview) {
3061
- const { env: resolvedEnv, usePreview, cacheKey } = this.resolveParams(componentId, env, preview);
3062
- const promise = Promise.resolve(config);
3063
- promise.resolved = config;
3064
- this.variants[cacheKey] = promise;
3065
- this.preloadedComponents[componentId] = { env: resolvedEnv, preview: usePreview };
3437
+ buildIsolationBridge(initialDataLiteral) {
3438
+ return "<script>\n (function () {\n var __initial = ".concat(initialDataLiteral, ";\n var __pending;\n // data-out (cta)\n window.myop_cta_handler = function (action, payload) {\n try { parent.postMessage({ __myop: true, type: 'myop:cta', action: action, payload: payload }, '*'); } catch (e) {}\n };\n function callInit(data) {\n if (typeof window.myop_init_interface === 'function') { window.myop_init_interface(data); return true; }\n return false;\n }\n // data-in (init updates)\n window.addEventListener('message', function (e) {\n var d = e.data;\n if (!d || d.__myop !== true) return;\n if (d.type === 'myop:init') { if (!callInit(d.data)) __pending = d.data; }\n });\n // first render: use a pending update if one arrived, else the baked initial data\n (function flushInitial() {\n var data = (__pending !== undefined) ? __pending : __initial;\n if (data === undefined) return;\n if (callInit(data)) { __pending = undefined; __initial = undefined; }\n else requestAnimationFrame(flushInitial);\n })();\n // resize (report content size)\n function contentHeight() {\n // Measure the BODY (content box). documentElement.scrollHeight includes\n // the iframe viewport \u2014 which we're driving \u2014 so it would report the\n // current frame height, not the content, and never shrink.\n var b = document.body;\n if (b) return Math.max(b.scrollHeight, b.offsetHeight);\n return document.documentElement.scrollHeight;\n }\n function postSize() {\n try {\n parent.postMessage({\n __myop: true, type: 'myop:size',\n height: contentHeight(),\n width: document.documentElement.scrollWidth\n }, '*');\n } catch (e) {}\n }\n // Observe the BODY (reflows with content) \u2014 not documentElement, whose box is\n // the iframe viewport and would feed back into a resize loop. A MutationObserver\n // catches content swaps (e.g. innerHTML re-render) that don't change body's box.\n if (window.ResizeObserver) {\n try { var __ro = new ResizeObserver(postSize); if (document.body) __ro.observe(document.body); } catch (e) {}\n }\n if (window.MutationObserver && document.body) {\n try { new MutationObserver(postSize).observe(document.body, { childList: true, subtree: true, attributes: true, characterData: true }); } catch (e) {}\n }\n window.addEventListener('load', postSize);\n postSize();\n })();\n <\/script>");
3066
3439
  }
3067
- // ============ V1 Methods (Legacy) ============
3068
3440
  /**
3069
- * Fetch a v1 component from a flow
3441
+ * Extract the web component script content from HTML
3070
3442
  */
3071
- async fetchComponentV1(componentId, flowId) {
3072
- if (flowId) {
3073
- const flow = await this.fetchFlow(flowId);
3074
- return flow.components.find((c) => c.type.id === componentId);
3075
- } else {
3076
- const flow = await this.fetchAutoFlow(componentId);
3077
- return flow.components[0];
3078
- }
3443
+ extractWebComponentScript(html) {
3444
+ const parser = new DOMParser();
3445
+ const doc = parser.parseFromString(html, "text/html");
3446
+ const script = doc.getElementById(MYOP_WEB_COMPONENT_SCRIPT_ID);
3447
+ return (script == null ? void 0 : script.textContent) || null;
3079
3448
  }
3080
- async fetchAutoFlow(componentId) {
3081
- if (!this.userFlows[componentId]) {
3082
- this.userFlows[componentId] = new Promise(
3083
- async (resolve, reject) => {
3084
- try {
3085
- const res = await fetch(
3086
- "".concat(this._baseUrl, "/flow?id=").concat(componentId, "&auto=true")
3087
- );
3088
- const json = await res.json();
3089
- resolve(json.item);
3090
- } catch (e) {
3091
- delete this.userFlows[componentId];
3092
- reject(e);
3093
- }
3094
- }
3095
- );
3449
+ /**
3450
+ * Execute the web component script on the host document.
3451
+ * Returns the custom element tag name this script registered.
3452
+ */
3453
+ executeWebComponentScript(scriptContent, scriptId) {
3454
+ const cachedTagName = extractedWebComponentScripts.get(scriptId);
3455
+ if (cachedTagName) {
3456
+ console.log("[MyopWebComponent] Script already executed for ".concat(scriptId, ", reusing <").concat(cachedTagName, ">"));
3457
+ return cachedTagName;
3096
3458
  }
3097
- return await this.userFlows[componentId];
3098
- }
3099
- async fetchFlow(flowId) {
3100
- if (!this.userFlows[flowId]) {
3101
- this.userFlows[flowId] = new Promise(
3102
- async (resolve, reject) => {
3103
- try {
3104
- const res = await fetch(
3105
- "".concat(this._baseUrl, "/flow?id=").concat(flowId, "&resolve=components")
3106
- );
3107
- const json = await res.json();
3108
- resolve(json.item);
3109
- } catch (e) {
3110
- delete this.userFlows[flowId];
3111
- reject(e);
3112
- }
3113
- }
3114
- );
3459
+ const script = document.createElement("script");
3460
+ script.textContent = scriptContent;
3461
+ script.id = "myop-extracted-".concat(scriptId);
3462
+ document.head.appendChild(script);
3463
+ const tagName = window.__MYOP_TAG_NAME__;
3464
+ if (tagName) {
3465
+ extractedWebComponentScripts.set(scriptId, tagName);
3115
3466
  }
3116
- return await this.userFlows[flowId];
3467
+ console.log("[MyopWebComponent] Executed web component script: ".concat(scriptId, " -> <").concat(tagName, ">"));
3468
+ return tagName;
3117
3469
  }
3118
- // ============ Backward Compatibility ============
3119
3470
  /**
3120
- * @deprecated Use fetchComponentV2 or fetchComponentV1 explicitly
3121
- * Defaults to v1 behavior for backward compatibility with existing code
3471
+ * Load a Myop Web Component (extracted and run on host document)
3122
3472
  */
3123
- async fetchComponent(componentId, flowId) {
3124
- return this.fetchComponentV1(componentId, flowId);
3473
+ async loadExtractedWebComponent(componentDefinition, skin, container, html, options) {
3474
+ const scriptContent = this.extractWebComponentScript(html);
3475
+ if (!scriptContent) {
3476
+ throw new Error('[MyopWebComponent] No <script id="myop-web-component"> found in HTML');
3477
+ }
3478
+ const scriptId = "".concat(componentDefinition.id, "-").concat(skin.id);
3479
+ const tagName = this.executeWebComponentScript(scriptContent, scriptId);
3480
+ if (!tagName) {
3481
+ throw new Error("[MyopWebComponent] Script did not set window.__MYOP_TAG_NAME__");
3482
+ }
3483
+ const domId = "myop-comp-".concat(uuidv4());
3484
+ const element = document.createElement(tagName);
3485
+ element.id = domId;
3486
+ if (options == null ? void 0 : options.hidden) {
3487
+ element.style.opacity = "0";
3488
+ element.style.position = "absolute";
3489
+ element.style.height = "0";
3490
+ element.style.width = "0";
3491
+ element.style.pointerEvents = "none";
3492
+ element.style.visibility = "hidden";
3493
+ }
3494
+ if (!container.querySelector('[id^="myop-comp-"]')) {
3495
+ container.innerHTML = "";
3496
+ }
3497
+ this.appendChild(container, element, options);
3498
+ const component = new MyopExtractedWebComponent(
3499
+ componentDefinition,
3500
+ skin,
3501
+ container,
3502
+ element,
3503
+ tagName,
3504
+ options
3505
+ );
3506
+ if ((options == null ? void 0 : options.data) !== void 0) {
3507
+ component.initWithData(options.data);
3508
+ }
3509
+ console.log("[MyopWebComponent] Loaded <".concat(tagName, "> into container"));
3510
+ return component;
3125
3511
  }
3126
3512
  };
3127
- __publicField(_CloudRepository, "_main", null);
3128
- var CloudRepository = _CloudRepository;
3129
-
3130
- // src/helpers/cloudRepositoryHelpers.ts
3131
- var _cloudRepository = CloudRepository.Main;
3132
3513
 
3133
3514
  // src/host/myopOverride.ts
3134
3515
  var checkMyopOverride = async (componentConfig) => {
@@ -3224,8 +3605,22 @@ var HostSDK = class {
3224
3605
  }
3225
3606
  });
3226
3607
  });
3608
+ // A component's origin is whatever its iframe URL is, so an origin allowlist can't work here.
3609
+ // Instead only frames this SDK created (or adopted) may talk to it. `send()` dispatches
3610
+ // synthetic events, which have no source.
3611
+ __publicField(this, "isTrustedSource", (source) => {
3612
+ if (!source || source === window) return true;
3613
+ const isOwnFrame = (component) => {
3614
+ const element = component.element;
3615
+ return (element == null ? void 0 : element.nodeName) === "IFRAME" && element.contentWindow === source;
3616
+ };
3617
+ return this.components.some(isOwnFrame) || this.components2init.some(isOwnFrame);
3618
+ });
3227
3619
  __publicField(this, "incomingMessageHandler", (event) => {
3228
- if (event.origin === document.location.origin) {
3620
+ var _a;
3621
+ if (!((_a = event.data) == null ? void 0 : _a.myop)) return;
3622
+ if (!this.isTrustedSource(event.source)) {
3623
+ return;
3229
3624
  }
3230
3625
  if (event.data.myop) {
3231
3626
  if (event.data.direction !== MessageDirection.up && event.data.messageType !== MyopMessages.EnvelopedMessage) {
@@ -3241,8 +3636,8 @@ var HostSDK = class {
3241
3636
  if (myopMessage.messageType === MyopMessages.InitResponse) {
3242
3637
  this.components2init = this.components2init.filter((component2) => component2.id !== myopMessage.content.id);
3243
3638
  const initiatedComponent = this.components.find((component2) => {
3244
- var _a;
3245
- return component2.id === ((_a = myopMessage.content) == null ? void 0 : _a.id);
3639
+ var _a2;
3640
+ return component2.id === ((_a2 = myopMessage.content) == null ? void 0 : _a2.id);
3246
3641
  });
3247
3642
  initiatedComponent == null ? void 0 : initiatedComponent.setInitiated();
3248
3643
  return;
@@ -3295,7 +3690,10 @@ var HostSDK = class {
3295
3690
  });
3296
3691
  __publicField(this, "init", (customComponentLoaderCreators) => {
3297
3692
  if (this.initiated) {
3298
- throw new Error("hostSDK already initiated");
3693
+ if (customComponentLoaderCreators == null ? void 0 : customComponentLoaderCreators.length) {
3694
+ this._registerLoaders(customComponentLoaderCreators);
3695
+ }
3696
+ return;
3299
3697
  }
3300
3698
  const basicLoaders = [
3301
3699
  new MyopIframeLoader(),
@@ -3306,16 +3704,7 @@ var HostSDK = class {
3306
3704
  ...basicLoaders
3307
3705
  ];
3308
3706
  if (customComponentLoaderCreators) {
3309
- const loaderByType = {};
3310
- customComponentLoaderCreators.forEach((customComponentLoaderCreator) => {
3311
- const customLoader = new customComponentLoaderCreator(this.componentsLoaders);
3312
- loaderByType[customLoader.type] = customLoader;
3313
- });
3314
- this.componentsLoaders.forEach((loader) => {
3315
- if (!loaderByType[loader.type])
3316
- loaderByType[loader.type] = loader;
3317
- });
3318
- this.componentsLoaders = Object.values(loaderByType);
3707
+ this._registerLoaders(customComponentLoaderCreators);
3319
3708
  }
3320
3709
  window.addEventListener(
3321
3710
  "message",
@@ -3325,6 +3714,18 @@ var HostSDK = class {
3325
3714
  console.log("myop hostSDK initiated", document.location.href);
3326
3715
  this.initiated = true;
3327
3716
  });
3717
+ __publicField(this, "_registerLoaders", (customComponentLoaderCreators) => {
3718
+ const loaderByType = {};
3719
+ customComponentLoaderCreators.forEach((customComponentLoaderCreator) => {
3720
+ const customLoader = new customComponentLoaderCreator(this.componentsLoaders);
3721
+ loaderByType[customLoader.type] = customLoader;
3722
+ });
3723
+ this.componentsLoaders.forEach((loader) => {
3724
+ if (!loaderByType[loader.type])
3725
+ loaderByType[loader.type] = loader;
3726
+ });
3727
+ this.componentsLoaders = Object.values(loaderByType);
3728
+ });
3328
3729
  __publicField(this, "_getSkinIdBySkinSelector", async (skinSelector, experiences = [], hooks) => {
3329
3730
  var _a;
3330
3731
  async function asyncFind(array, predicate) {
@@ -3505,6 +3906,13 @@ if (!window.myop.hostSDK) {
3505
3906
  }
3506
3907
  window.myop.hostSDK = _hostSDK;
3507
3908
  }
3909
+ if (window.myop && !window.myop.delivery) {
3910
+ const api = (opts) => delivery(opts);
3911
+ api.stats = deliveryStats;
3912
+ api.off = deliveryOff;
3913
+ api.refresh = deliveryRefresh;
3914
+ window.myop.delivery = api;
3915
+ }
3508
3916
  var hostSDK = window.myop.hostSDK;
3509
3917
  defineDynamicImport("hostSDK", hostSDK);
3510
3918
 
@@ -3728,7 +4136,10 @@ var IframeSDK = class extends BaseMyopSDK {
3728
4136
  new CreateRefComponentMessageHandler2(this.messageToHost)
3729
4137
  ]);
3730
4138
  __publicField(this, "init", () => {
3731
- window.onmessage = this.handleIncomeMessages.bind(this);
4139
+ window.onmessage = (event) => {
4140
+ if (event.source && event.source !== window.parent && event.source !== window) return;
4141
+ this.handleIncomeMessages(event);
4142
+ };
3732
4143
  this.messageToHost(new InitRequest());
3733
4144
  });
3734
4145
  if (!window.myop) {