@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
@@ -38,12 +38,17 @@ var helpers_exports = {};
38
38
  __export(helpers_exports, {
39
39
  CloudRepository: () => CloudRepository,
40
40
  ComponentConfig: () => ComponentConfig,
41
+ DELIVERY_HOST: () => DELIVERY_HOST,
41
42
  SkinConfig: () => SkinConfig,
42
43
  addClass: () => addClass,
43
44
  applyStylesheet: () => applyStylesheet,
44
45
  applyStylesheetLink: () => applyStylesheetLink,
45
46
  changeSkinUrlByTokenizer: () => changeSkinUrlByTokenizer,
46
47
  createIframeSkinByTokenizer: () => createIframeSkinByTokenizer,
48
+ delivery: () => delivery,
49
+ deliveryOff: () => deliveryOff,
50
+ deliveryRefresh: () => deliveryRefresh,
51
+ deliveryStats: () => deliveryStats,
47
52
  enableLocalDev: () => enableLocalDev,
48
53
  enableSelfHosted: () => enableSelfHosted,
49
54
  exec: () => exec,
@@ -357,6 +362,114 @@ var PropConfigBehaviorTypes = {
357
362
  "code": "code"
358
363
  };
359
364
 
365
+ // src/helpers/deliveryStore.ts
366
+ var DB_NAME = "myop-delivery";
367
+ var STORE = "components";
368
+ var DB_VERSION = 1;
369
+ var dbPromise = null;
370
+ function openDb() {
371
+ if (dbPromise) return dbPromise;
372
+ dbPromise = new Promise((resolve) => {
373
+ try {
374
+ if (typeof indexedDB === "undefined") return resolve(null);
375
+ const req = indexedDB.open(DB_NAME, DB_VERSION);
376
+ req.onupgradeneeded = () => {
377
+ const db = req.result;
378
+ if (!db.objectStoreNames.contains(STORE)) db.createObjectStore(STORE, { keyPath: "key" });
379
+ };
380
+ req.onsuccess = () => resolve(req.result);
381
+ req.onerror = () => resolve(null);
382
+ req.onblocked = () => resolve(null);
383
+ } catch (e) {
384
+ resolve(null);
385
+ }
386
+ });
387
+ return dbPromise;
388
+ }
389
+ function tx(mode, fn) {
390
+ return openDb().then((db) => new Promise((resolve) => {
391
+ if (!db) return resolve(null);
392
+ try {
393
+ const t = db.transaction(STORE, mode);
394
+ const req = fn(t.objectStore(STORE));
395
+ req.onsuccess = () => resolve(req.result);
396
+ req.onerror = () => resolve(null);
397
+ t.onabort = () => resolve(null);
398
+ } catch (e) {
399
+ resolve(null);
400
+ }
401
+ }));
402
+ }
403
+ var HINT_PREFIX = "myop.d.";
404
+ function writeHint(key, tag, expiresAt) {
405
+ try {
406
+ localStorage.setItem(HINT_PREFIX + key, JSON.stringify({ t: tag, e: expiresAt }));
407
+ } catch (e) {
408
+ }
409
+ }
410
+ function clearHint(key) {
411
+ try {
412
+ localStorage.removeItem(HINT_PREFIX + key);
413
+ } catch (e) {
414
+ }
415
+ }
416
+ var memory = /* @__PURE__ */ new Map();
417
+ var deliveryStore = {
418
+ key(host, componentId, env, preview, opt) {
419
+ return "".concat(host, "|").concat(componentId, "|").concat(env, "|").concat(preview ? "preview" : "live", "|").concat(opt || "raw");
420
+ },
421
+ async get(key) {
422
+ const m = memory.get(key);
423
+ if (m) return m;
424
+ const e = await tx("readonly", (s) => s.get(key));
425
+ if (e) {
426
+ memory.set(key, e);
427
+ writeHint(key, e.tag, e.storedAt + e.windowMs);
428
+ }
429
+ return e;
430
+ },
431
+ async put(entry) {
432
+ memory.set(entry.key, entry);
433
+ writeHint(entry.key, entry.tag, entry.storedAt + entry.windowMs);
434
+ await tx("readwrite", (s) => s.put(entry));
435
+ },
436
+ async drop(key) {
437
+ memory.delete(key);
438
+ clearHint(key);
439
+ await tx("readwrite", (s) => s.delete(key));
440
+ },
441
+ /** Still usable without asking the server? */
442
+ fresh(e) {
443
+ return !!e && Date.now() - e.storedAt < e.windowMs;
444
+ },
445
+ /**
446
+ * The window this response licenses: the server's freshness minus what a CDN already
447
+ * spent. Returns 0 when the server said nothing about caching, which means "do not
448
+ * hold this" — the safe reading, and what production sends today.
449
+ */
450
+ windowFrom(headers) {
451
+ const cc = headers.get("cache-control") || "";
452
+ if (/no-store|no-cache/i.test(cc)) return 0;
453
+ const m = /s-maxage\s*=\s*(\d+)/i.exec(cc) || /max-age\s*=\s*(\d+)/i.exec(cc);
454
+ if (!m) return 0;
455
+ const total = Number(m[1]);
456
+ const age = Number(headers.get("age") || 0);
457
+ return Math.max(0, total - (isNaN(age) ? 0 : age)) * 1e3;
458
+ },
459
+ /** For tests and for `refresh()`: forget everything this browser holds. */
460
+ async clear() {
461
+ memory.clear();
462
+ try {
463
+ for (let i = localStorage.length - 1; i >= 0; i--) {
464
+ const k = localStorage.key(i);
465
+ if (k && k.startsWith(HINT_PREFIX)) localStorage.removeItem(k);
466
+ }
467
+ } catch (e) {
468
+ }
469
+ await tx("readwrite", (s) => s.clear());
470
+ }
471
+ };
472
+
360
473
  // src/helpers/CloudRepository.ts
361
474
  var convertV2VariantToComponentConfig = (config) => {
362
475
  return {
@@ -442,6 +555,45 @@ var _CloudRepository = class _CloudRepository {
442
555
  __publicField(this, "_envExplicitlySet", false);
443
556
  __publicField(this, "_staticMode", false);
444
557
  __publicField(this, "_queryParams", {});
558
+ /**
559
+ * Opt-in delivery settings, set by myop.delivery(). Off means: this file behaves
560
+ * EXACTLY as it did before — same host, same URL, same everything.
561
+ */
562
+ __publicField(this, "_delivery", { minify: false, host: null, cache: false });
563
+ /**
564
+ * What actually happened, for myop.delivery.stats(). `fellBack` is the number that
565
+ * matters: it is how you find out the lane is unhealthy without waiting for a
566
+ * bug report, because a fallback is INVISIBLE to the page (it still renders).
567
+ */
568
+ __publicField(this, "_deliveryStats", {
569
+ requests: 0,
570
+ viaDelivery: 0,
571
+ fellBack: 0,
572
+ lastError: null,
573
+ /** where the last loads were answered from — the number the client window exists to move */
574
+ fromMemory: 0,
575
+ fromStorage: 0,
576
+ fromNetwork: 0,
577
+ /** re-checks after the window ended, and how many were answered "nothing changed" */
578
+ revalidations: 0,
579
+ unchanged: 0,
580
+ bytesSaved: 0
581
+ });
582
+ /**
583
+ * Local serves waiting to be reported: componentId -> {env, count}.
584
+ *
585
+ * THE MEASUREMENT PROBLEM THIS SOLVES. The better this cache works, the more usage
586
+ * vanishes: a visitor inside their freshness window renders the page with no request,
587
+ * so neither the server nor the CDN can see them. Left alone, a successful rollout
588
+ * looks in the ledger exactly like customers leaving.
589
+ *
590
+ * So local serves are counted here and reported afterwards, batched, on a path that
591
+ * never blocks rendering: flushed when the page is hidden (keepalive, so it survives
592
+ * the tab closing) and opportunistically whenever the SDK is talking to the server
593
+ * anyway. Nothing is sent while the page is loading, which is the point of the window.
594
+ */
595
+ __publicField(this, "_localServes", /* @__PURE__ */ new Map());
596
+ __publicField(this, "_flushBound", false);
445
597
  }
446
598
  static get Main() {
447
599
  const globalMain = getGlobalMain();
@@ -454,6 +606,120 @@ var _CloudRepository = class _CloudRepository {
454
606
  }
455
607
  return _CloudRepository._main;
456
608
  }
609
+ /**
610
+ * How big the component we are NOT re-downloading is. Deliberately measured from what
611
+ * we hold rather than from Content-Length: once a response is compressed the header is
612
+ * not readable cross-origin, so reading it there reported 0 every single time.
613
+ */
614
+ static sizeOfHeld(e) {
615
+ var _a, _b, _c, _d, _e;
616
+ try {
617
+ 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;
618
+ } catch (e2) {
619
+ return 0;
620
+ }
621
+ }
622
+ /**
623
+ * Turn on the opt-in delivery path.
624
+ *
625
+ * Opting in IS choosing the delivery stack: this points /consume at `host` (a second,
626
+ * complete stack reading the same data) and asks for the minified artifact. Leave this
627
+ * uncalled and nothing about the SDK changes.
628
+ *
629
+ * Deliberately mutates THIS instance rather than constructing a new repository:
630
+ * CloudRepository.Main is a global singleton shared across bundles (see getGlobalMain),
631
+ * so a second instance would be ignored by every component already using the first.
632
+ */
633
+ setDelivery(opts = {}) {
634
+ if (opts.minify !== void 0) this._delivery.minify = !!opts.minify;
635
+ if (opts.host !== void 0) this._delivery.host = opts.host;
636
+ if (opts.cache !== void 0) this._delivery.cache = !!opts.cache;
637
+ }
638
+ deliveryStats() {
639
+ var _a;
640
+ return __spreadProps(__spreadValues({}, this._deliveryStats), {
641
+ host: (_a = this._delivery.host) != null ? _a : _CloudRepository.DEFAULT_HOST,
642
+ minify: this._delivery.minify,
643
+ cache: this._delivery.cache
644
+ });
645
+ }
646
+ /**
647
+ * Forget what this browser holds, so the next load asks the server.
648
+ * `refresh()` with no ids drops everything; the counterpart to the window for the case
649
+ * where a change has to be visible on THIS load — right after someone publishes.
650
+ */
651
+ async refreshDelivery() {
652
+ await deliveryStore.clear();
653
+ }
654
+ noteLocalServe(componentId, env) {
655
+ const cur = this._localServes.get(componentId);
656
+ if (cur) cur.count++;
657
+ else this._localServes.set(componentId, { env, count: 1 });
658
+ this.bindFlush();
659
+ }
660
+ /** Report on page-hide, which is the only event that reliably fires on mobile. */
661
+ bindFlush() {
662
+ if (this._flushBound || typeof document === "undefined") return;
663
+ this._flushBound = true;
664
+ const onHide = () => {
665
+ if (document.visibilityState === "hidden") this.flushLocalServes();
666
+ };
667
+ document.addEventListener("visibilitychange", onHide);
668
+ window.addEventListener("pagehide", () => this.flushLocalServes());
669
+ }
670
+ /**
671
+ * Send what we have. `keepalive` is what makes this work at page-hide: the request
672
+ * outlives the document. Failure is ignored — a lost usage report must never be
673
+ * visible to a visitor, and the next page load will report its own.
674
+ */
675
+ flushLocalServes() {
676
+ if (!this._localServes.size) return;
677
+ const batch = [...this._localServes.entries()].map(([id, v]) => "".concat(id, ":").concat(v.env, ":").concat(v.count)).join(",");
678
+ this._localServes.clear();
679
+ try {
680
+ const url = "".concat(this.deliveryHost(), "/clientUsage?b=").concat(encodeURIComponent(batch));
681
+ fetch(url, { keepalive: true, mode: "cors", credentials: "omit" }).catch(() => {
682
+ });
683
+ } catch (e) {
684
+ }
685
+ }
686
+ /** Where /consume should be fetched from right now. */
687
+ deliveryHost() {
688
+ return this._delivery.host || this._baseUrl;
689
+ }
690
+ /**
691
+ * Fetch a delivery URL with ONE fallback to the default host.
692
+ *
693
+ * The fallback is the kill switch: break or disable the lane and every client drifts
694
+ * back to cloud.myop.dev on its own, with no customer deploy. It must therefore be
695
+ * genuinely silent to the page and genuinely LOUD in stats() — a fallback that nobody
696
+ * counts is an outage nobody notices.
697
+ *
698
+ * Only transport failures and 5xx fall back. A 404 is an answer: the component really
699
+ * is missing, and retrying elsewhere would only hide it.
700
+ */
701
+ async deliveryFetch(path) {
702
+ var _a;
703
+ if (this._localServes.size) this.flushLocalServes();
704
+ const host = this.deliveryHost();
705
+ const primary = "".concat(host).concat(path);
706
+ this._deliveryStats.requests++;
707
+ const usingLane = host !== this._baseUrl;
708
+ try {
709
+ const res = await fetch(primary);
710
+ if (res.ok || res.status < 500 || !usingLane) {
711
+ if (usingLane && res.ok) this._deliveryStats.viaDelivery++;
712
+ return res;
713
+ }
714
+ this._deliveryStats.lastError = "HTTP ".concat(res.status, " from ").concat(host);
715
+ } catch (e) {
716
+ if (!usingLane) throw e;
717
+ this._deliveryStats.lastError = String((_a = e == null ? void 0 : e.message) != null ? _a : e);
718
+ }
719
+ this._deliveryStats.fellBack++;
720
+ console.warn("[myop] delivery host failed (".concat(this._deliveryStats.lastError, ") \u2014 falling back to ").concat(this._baseUrl));
721
+ return await fetch("".concat(this._baseUrl).concat(path));
722
+ }
457
723
  /**
458
724
  * Enable static mode for self-hosted component registries.
459
725
  * In static mode, URLs are constructed as file paths instead of query params:
@@ -549,12 +815,68 @@ var _CloudRepository = class _CloudRepository {
549
815
  const qs = new URLSearchParams(this._queryParams).toString();
550
816
  if (qs) url += "?".concat(qs);
551
817
  } else {
552
- url = "".concat(this._baseUrl, "/consume?id=").concat(componentId, "&env=").concat(env);
818
+ url = "/consume?id=".concat(componentId, "&env=").concat(env);
553
819
  if (usePreview) {
554
820
  url += "&preview=true";
555
821
  }
822
+ if (this._delivery.minify && !usePreview) {
823
+ url += "&opt=min";
824
+ }
825
+ }
826
+ const cacheable = this._delivery.cache && !this._staticMode && !usePreview;
827
+ const storeKey = cacheable ? deliveryStore.key(
828
+ this.deliveryHost(),
829
+ componentId,
830
+ env,
831
+ usePreview,
832
+ this._delivery.minify ? "min" : "raw"
833
+ ) : "";
834
+ let held = cacheable ? await deliveryStore.get(storeKey) : null;
835
+ if (held && deliveryStore.fresh(held)) {
836
+ this._deliveryStats.fromStorage++;
837
+ this.noteLocalServe(componentId, env);
838
+ this.variants[cacheKey].resolved = held.config;
839
+ resolve(held.config);
840
+ return;
841
+ }
842
+ if (held && held.tag) {
843
+ const checkUrl = "/consume?ids=".concat(componentId, "&env=").concat(env) + (this._delivery.minify ? "&opt=min" : "") + "&have=".concat(encodeURIComponent(held.tag));
844
+ this._deliveryStats.revalidations++;
845
+ try {
846
+ const check = await this.deliveryFetch(checkUrl);
847
+ if (check.ok) {
848
+ const body = await check.json();
849
+ if ((body.unchanged || []).includes(componentId)) {
850
+ this._deliveryStats.unchanged++;
851
+ this._deliveryStats.bytesSaved += _CloudRepository.sizeOfHeld(held);
852
+ held.storedAt = Date.now();
853
+ held.windowMs = deliveryStore.windowFrom(check.headers) || held.windowMs;
854
+ await deliveryStore.put(held);
855
+ this.variants[cacheKey].resolved = held.config;
856
+ resolve(held.config);
857
+ return;
858
+ }
859
+ const item = (body.items || [])[0];
860
+ if (item) {
861
+ const cfg = convertV2VariantToComponentConfig(item);
862
+ this._deliveryStats.fromNetwork++;
863
+ await deliveryStore.put({
864
+ key: storeKey,
865
+ config: cfg,
866
+ tag: item.tag || null,
867
+ storedAt: Date.now(),
868
+ windowMs: deliveryStore.windowFrom(check.headers),
869
+ host: this.deliveryHost()
870
+ });
871
+ this.variants[cacheKey].resolved = cfg;
872
+ resolve(cfg);
873
+ return;
874
+ }
875
+ }
876
+ } catch (e) {
877
+ }
556
878
  }
557
- const res = await fetch(url);
879
+ const res = this._staticMode ? await fetch(url) : await this.deliveryFetch(url);
558
880
  if (!res.ok) {
559
881
  reject(new Error('Failed to load component "'.concat(componentId, '" (').concat(res.status).concat(this._staticMode ? " from ".concat(url) : "", ")")));
560
882
  return;
@@ -570,6 +892,17 @@ var _CloudRepository = class _CloudRepository {
570
892
  return;
571
893
  }
572
894
  const componentConfig = convertV2VariantToComponentConfig(variant);
895
+ this._deliveryStats.fromNetwork++;
896
+ if (cacheable) {
897
+ await deliveryStore.put({
898
+ key: storeKey,
899
+ config: componentConfig,
900
+ tag: variant.tag || null,
901
+ storedAt: Date.now(),
902
+ windowMs: deliveryStore.windowFrom(res.headers),
903
+ host: this.deliveryHost()
904
+ });
905
+ }
573
906
  this.variants[cacheKey].resolved = componentConfig;
574
907
  resolve(componentConfig);
575
908
  } catch (e) {
@@ -704,6 +1037,8 @@ var _CloudRepository = class _CloudRepository {
704
1037
  }
705
1038
  };
706
1039
  __publicField(_CloudRepository, "_main", null);
1040
+ /** The host every request goes to unless delivery() moved it. Never changes. */
1041
+ __publicField(_CloudRepository, "DEFAULT_HOST", "https://cloud.myop.dev");
707
1042
  var CloudRepository = _CloudRepository;
708
1043
  var v2_CloudRepository = CloudRepository;
709
1044
 
@@ -734,6 +1069,26 @@ var enableSelfHosted = (cdnUrl, queryParams) => {
734
1069
  _cloudRepository.setQueryParams(queryParams);
735
1070
  }
736
1071
  };
1072
+ var DELIVERY_HOST = "https://delivery.myop.dev";
1073
+ var delivery = (opts = {}) => {
1074
+ var _a, _b;
1075
+ const wantsLane = opts.minify !== false || opts.cache === true;
1076
+ _cloudRepository.setDelivery({
1077
+ minify: (_a = opts.minify) != null ? _a : false,
1078
+ cache: (_b = opts.cache) != null ? _b : false,
1079
+ host: opts.host !== void 0 ? opts.host : wantsLane ? DELIVERY_HOST : null
1080
+ });
1081
+ return deliveryStats();
1082
+ };
1083
+ var deliveryRefresh = async () => {
1084
+ await _cloudRepository.refreshDelivery();
1085
+ return deliveryStats();
1086
+ };
1087
+ var deliveryStats = () => _cloudRepository.deliveryStats();
1088
+ var deliveryOff = () => {
1089
+ _cloudRepository.setDelivery({ minify: false, cache: false, host: null });
1090
+ return deliveryStats();
1091
+ };
737
1092
  var setCloudRepository = (repository) => {
738
1093
  _cloudRepository = repository;
739
1094
  };
@@ -759,12 +1114,17 @@ defineDynamicImport("MyopHelpers", helpers_exports);
759
1114
  export {
760
1115
  CloudRepository,
761
1116
  ComponentConfig,
1117
+ DELIVERY_HOST,
762
1118
  SkinConfig,
763
1119
  addClass,
764
1120
  applyStylesheet,
765
1121
  applyStylesheetLink,
766
1122
  changeSkinUrlByTokenizer,
767
1123
  createIframeSkinByTokenizer,
1124
+ delivery,
1125
+ deliveryOff,
1126
+ deliveryRefresh,
1127
+ deliveryStats,
768
1128
  enableLocalDev,
769
1129
  enableSelfHosted,
770
1130
  exec,