@matterfact/embed 0.15.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -12,6 +12,114 @@ var __export = (target, all) => {
12
12
  __defProp(target, name, { get: all[name], enumerable: true });
13
13
  };
14
14
 
15
+ // src/deeplink.ts
16
+ var deeplink_exports = {};
17
+ __export(deeplink_exports, {
18
+ compileDeeplinkFormat: () => compileDeeplinkFormat,
19
+ installDeeplinkWatch: () => installDeeplinkWatch
20
+ });
21
+ function compileDeeplinkFormat(format) {
22
+ const first = format.indexOf(PLACEHOLDER);
23
+ if (first < 0 || format.indexOf(PLACEHOLDER, first + 1) >= 0) return null;
24
+ let f;
25
+ try {
26
+ f = new URL(format.replace(PLACEHOLDER, SENTINEL));
27
+ } catch {
28
+ return null;
29
+ }
30
+ if (f.protocol !== "https:" && f.protocol !== "http:") return null;
31
+ const keys = [...f.searchParams.keys()];
32
+ if (new Set(keys).size !== keys.length) return null;
33
+ const fPath = trimSlash(f.pathname);
34
+ const segs = fPath.split("/");
35
+ const segIdx = segs.indexOf(SENTINEL);
36
+ let queryKey = null;
37
+ for (const [k, v] of f.searchParams) {
38
+ if (v === SENTINEL) queryKey = k;
39
+ }
40
+ if (segIdx >= 0 === (queryKey !== null)) return null;
41
+ return (href) => {
42
+ let u;
43
+ try {
44
+ u = new URL(href);
45
+ } catch {
46
+ return null;
47
+ }
48
+ if (u.origin !== f.origin) return null;
49
+ const uPath = trimSlash(u.pathname);
50
+ let token;
51
+ if (segIdx >= 0) {
52
+ const us = uPath.split("/");
53
+ if (us.length !== segs.length) return null;
54
+ for (let i = 0; i < segs.length; i++) {
55
+ if (i !== segIdx && us[i] !== segs[i]) return null;
56
+ }
57
+ token = us[segIdx];
58
+ for (const [k, v] of f.searchParams) {
59
+ if (u.searchParams.get(k) !== v) return null;
60
+ }
61
+ } else {
62
+ if (uPath !== fPath) return null;
63
+ for (const [k, v] of f.searchParams) {
64
+ if (v === SENTINEL) continue;
65
+ if (u.searchParams.get(k) !== v) return null;
66
+ }
67
+ token = u.searchParams.get(queryKey);
68
+ }
69
+ return token && TOKEN_RE.test(token) ? token : null;
70
+ };
71
+ }
72
+ function installDeeplinkWatch(format, onChange2) {
73
+ const match = compileDeeplinkFormat(format);
74
+ if (!match) return () => {
75
+ };
76
+ let last = null;
77
+ let stopped = false;
78
+ const fire = () => {
79
+ if (stopped) return;
80
+ try {
81
+ const token = match(location.href);
82
+ if (token !== last) {
83
+ last = token;
84
+ onChange2(token);
85
+ }
86
+ } catch (err) {
87
+ console.warn("[mf-embed] deeplink match failed", err);
88
+ }
89
+ };
90
+ const origPush = history.pushState;
91
+ const origReplace = history.replaceState;
92
+ const ourPush = function(...args) {
93
+ const r = origPush.apply(this, args);
94
+ fire();
95
+ return r;
96
+ };
97
+ const ourReplace = function(...args) {
98
+ const r = origReplace.apply(this, args);
99
+ fire();
100
+ return r;
101
+ };
102
+ history.pushState = ourPush;
103
+ history.replaceState = ourReplace;
104
+ window.addEventListener("popstate", fire);
105
+ fire();
106
+ return () => {
107
+ stopped = true;
108
+ window.removeEventListener("popstate", fire);
109
+ if (history.pushState === ourPush) history.pushState = origPush;
110
+ if (history.replaceState === ourReplace) history.replaceState = origReplace;
111
+ };
112
+ }
113
+ var PLACEHOLDER, SENTINEL, TOKEN_RE, trimSlash;
114
+ var init_deeplink = __esm({
115
+ "src/deeplink.ts"() {
116
+ PLACEHOLDER = "{share_token}";
117
+ SENTINEL = "mf-share-token-sentinel";
118
+ TOKEN_RE = /^[A-Za-z0-9._~-]{4,256}$/;
119
+ trimSlash = (path) => path.length > 1 && path.endsWith("/") ? path.slice(0, -1) : path;
120
+ }
121
+ });
122
+
15
123
  // src/pageContextMode.ts
16
124
  function parsePageContextMode(value) {
17
125
  if (typeof value === "boolean") return value ? "full" : "off";
@@ -1583,7 +1691,7 @@ var init_context = __esm({
1583
1691
  });
1584
1692
 
1585
1693
  // src/protocol.ts
1586
- var PROTOCOL_VERSION = 3;
1694
+ var PROTOCOL_VERSION = 4;
1587
1695
  var CHANNEL = "mf-embed";
1588
1696
  function envelope(payload, id) {
1589
1697
  return {
@@ -1806,6 +1914,7 @@ function dockBox(g, vw, _vh) {
1806
1914
 
1807
1915
  // src/loader.ts
1808
1916
  var DEFAULT_ORIGIN = "https://app.matterfact.com";
1917
+ var EMBED_VERSION = "0.17.0";
1809
1918
  function devRequested() {
1810
1919
  try {
1811
1920
  if (new URLSearchParams(location.search).get("mfdev") === "1") return true;
@@ -1850,7 +1959,10 @@ function readConfig() {
1850
1959
  theme: el?.dataset.theme || "auto",
1851
1960
  surface: el?.dataset.surface || "",
1852
1961
  container,
1853
- pageContext
1962
+ pageContext,
1963
+ // `data-share-deeplink-format="https://…/{share_token}"` — the script-tag twin of
1964
+ // the React `shareDeeplinkFormat` prop.
1965
+ shareDeeplinkFormat: el?.dataset.shareDeeplinkFormat
1854
1966
  // No `data-dev` — see the `dev` field's doc comment: the URL trigger is the
1855
1967
  // point for the script-tag path, so there is deliberately no script-tag knob here.
1856
1968
  // No `data-actions` either: the action policy lives entirely in the lazy chunk,
@@ -1931,6 +2043,8 @@ var EmbedHost = class {
1931
2043
  this.inline = !!config.container;
1932
2044
  }
1933
2045
  mount() {
2046
+ const w = window;
2047
+ (w.matterfact ?? (w.matterfact = {})).embedVersion = EMBED_VERSION;
1934
2048
  const host = document.createElement("div");
1935
2049
  this.hostEl = host;
1936
2050
  host.id = "matterfact-embed";
@@ -1974,7 +2088,7 @@ var EmbedHost = class {
1974
2088
  "sandbox",
1975
2089
  "allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox allow-downloads"
1976
2090
  );
1977
- iframe.setAttribute("allow", "microphone; clipboard-write");
2091
+ iframe.setAttribute("allow", "microphone; clipboard-write; web-share");
1978
2092
  iframe.src = `${this.config.origin}/embed/chat?k=${encodeURIComponent(
1979
2093
  this.config.publishableKey
1980
2094
  )}&o=${encodeURIComponent(location.origin)}` + (this.config.surface ? `&s=${encodeURIComponent(this.config.surface)}` : "") + // Either trigger works: the URL param (no redeploy needed) OR the config's
@@ -1987,6 +2101,17 @@ var EmbedHost = class {
1987
2101
  window.addEventListener("resize", this.onViewportResize);
1988
2102
  this.place();
1989
2103
  }
2104
+ const fmt = this.config.shareDeeplinkFormat;
2105
+ if (fmt) {
2106
+ this.send({ type: "host.deeplinkFormat", format: fmt });
2107
+ void Promise.resolve().then(() => (init_deeplink(), deeplink_exports)).then((m) => {
2108
+ if (this.destroyed) return;
2109
+ this.deeplinkStop = m.installDeeplinkWatch(fmt, (token) => {
2110
+ this.send({ type: "host.openShare", token });
2111
+ });
2112
+ }).catch(() => {
2113
+ });
2114
+ }
1990
2115
  }
1991
2116
  /**
1992
2117
  * Test seam. The shadow root is CLOSED, so a test cannot reach the iframe to forge a
@@ -2409,6 +2534,9 @@ var EmbedHost = class {
2409
2534
  this.iframe = null;
2410
2535
  this.shadow = null;
2411
2536
  this.ready = false;
2537
+ this.destroyed = true;
2538
+ this.deeplinkStop?.();
2539
+ this.deeplinkStop = null;
2412
2540
  void this.context?.then((m) => m.stop());
2413
2541
  }
2414
2542
  };