@matterfact/embed 0.16.0 → 0.18.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/react.cjs CHANGED
@@ -22,6 +22,191 @@ var __copyProps = (to, from, except, desc) => {
22
22
  };
23
23
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
24
24
 
25
+ // src/deeplink.ts
26
+ var deeplink_exports = {};
27
+ __export(deeplink_exports, {
28
+ compileDeeplink: () => compileDeeplink,
29
+ compileDeeplinkFormat: () => compileDeeplinkFormat,
30
+ installDeeplinkWatch: () => installDeeplinkWatch
31
+ });
32
+ function compileDeeplinkFormat(format) {
33
+ return compileDeeplink(format)?.match ?? null;
34
+ }
35
+ function compileDeeplink(format) {
36
+ if (format.includes(PATH_TOKEN)) return compileDynamic(format);
37
+ const match = compileStatic(format);
38
+ return match ? { match, mintFormat: () => format, dynamic: false } : null;
39
+ }
40
+ function compileDynamic(format) {
41
+ const pi = format.indexOf(PATH_TOKEN);
42
+ if (format.indexOf(PATH_TOKEN, pi + 1) >= 0) return null;
43
+ let prefix = format.slice(0, pi);
44
+ if (prefix.endsWith("/")) prefix = prefix.slice(0, -1);
45
+ const suffix = format.slice(pi + PATH_TOKEN.length);
46
+ let po;
47
+ try {
48
+ po = new URL(prefix);
49
+ } catch {
50
+ return null;
51
+ }
52
+ if (po.protocol !== "https:" && po.protocol !== "http:") return null;
53
+ if (po.origin !== prefix) return null;
54
+ if (!suffix.startsWith("?")) return null;
55
+ const c1 = suffix.indexOf(PLACEHOLDER);
56
+ if (c1 < 0 || suffix.indexOf(PLACEHOLDER, c1 + 1) >= 0) return null;
57
+ let probe;
58
+ try {
59
+ probe = new URL(prefix + "/__mfp__" + suffix.replace(PLACEHOLDER, SENTINEL));
60
+ } catch {
61
+ return null;
62
+ }
63
+ const keys = [...probe.searchParams.keys()];
64
+ if (new Set(keys).size !== keys.length) return null;
65
+ let queryKey = null;
66
+ for (const [k, v] of probe.searchParams) {
67
+ if (v === SENTINEL) queryKey = k;
68
+ }
69
+ if (!queryKey) return null;
70
+ const match = (href) => {
71
+ let u;
72
+ try {
73
+ u = new URL(href);
74
+ } catch {
75
+ return null;
76
+ }
77
+ if (u.origin !== prefix) return null;
78
+ for (const [k, v] of probe.searchParams) {
79
+ if (v === SENTINEL) continue;
80
+ if (u.searchParams.get(k) !== v) return null;
81
+ }
82
+ const token = u.searchParams.get(queryKey);
83
+ return token && TOKEN_RE.test(token) ? token : null;
84
+ };
85
+ return {
86
+ match,
87
+ mintFormat: (pathname) => prefix + pathname + suffix,
88
+ dynamic: true
89
+ };
90
+ }
91
+ function compileStatic(format) {
92
+ const first = format.indexOf(PLACEHOLDER);
93
+ if (first < 0 || format.indexOf(PLACEHOLDER, first + 1) >= 0) return null;
94
+ let f;
95
+ try {
96
+ f = new URL(format.replace(PLACEHOLDER, SENTINEL));
97
+ } catch {
98
+ return null;
99
+ }
100
+ if (f.protocol !== "https:" && f.protocol !== "http:") return null;
101
+ const keys = [...f.searchParams.keys()];
102
+ if (new Set(keys).size !== keys.length) return null;
103
+ const fPath = trimSlash(f.pathname);
104
+ const segs = fPath.split("/");
105
+ const segIdx = segs.indexOf(SENTINEL);
106
+ let queryKey = null;
107
+ for (const [k, v] of f.searchParams) {
108
+ if (v === SENTINEL) queryKey = k;
109
+ }
110
+ if (segIdx >= 0 === (queryKey !== null)) return null;
111
+ return (href) => {
112
+ let u;
113
+ try {
114
+ u = new URL(href);
115
+ } catch {
116
+ return null;
117
+ }
118
+ if (u.origin !== f.origin) return null;
119
+ const uPath = trimSlash(u.pathname);
120
+ let token;
121
+ if (segIdx >= 0) {
122
+ const us = uPath.split("/");
123
+ if (us.length !== segs.length) return null;
124
+ for (let i = 0; i < segs.length; i++) {
125
+ if (i !== segIdx && us[i] !== segs[i]) return null;
126
+ }
127
+ token = us[segIdx];
128
+ for (const [k, v] of f.searchParams) {
129
+ if (u.searchParams.get(k) !== v) return null;
130
+ }
131
+ } else {
132
+ if (uPath !== fPath) return null;
133
+ for (const [k, v] of f.searchParams) {
134
+ if (v === SENTINEL) continue;
135
+ if (u.searchParams.get(k) !== v) return null;
136
+ }
137
+ token = u.searchParams.get(queryKey);
138
+ }
139
+ return token && TOKEN_RE.test(token) ? token : null;
140
+ };
141
+ }
142
+ function installDeeplinkWatch(format, onChange2, onFormat) {
143
+ const c = compileDeeplink(format);
144
+ if (!c) return () => {
145
+ };
146
+ let last = null;
147
+ let lastTokenPath = null;
148
+ let lastMintPath = null;
149
+ let stopped = false;
150
+ const emitFormat = () => {
151
+ if (!onFormat) return;
152
+ const p = location.pathname;
153
+ if (lastMintPath === null || c.dynamic && p !== lastMintPath) {
154
+ lastMintPath = p;
155
+ onFormat(c.mintFormat(p));
156
+ }
157
+ };
158
+ const fire = () => {
159
+ if (stopped) return;
160
+ try {
161
+ emitFormat();
162
+ const token = c.match(location.href);
163
+ if (token === null && last !== null && trimSlash(location.pathname) === lastTokenPath) {
164
+ return;
165
+ }
166
+ if (token !== last) {
167
+ last = token;
168
+ if (token !== null) lastTokenPath = trimSlash(location.pathname);
169
+ onChange2(token);
170
+ }
171
+ } catch (err) {
172
+ console.warn("[mf-embed] deeplink match failed", err);
173
+ }
174
+ };
175
+ const origPush = history.pushState;
176
+ const origReplace = history.replaceState;
177
+ const ourPush = function(...args) {
178
+ const r = origPush.apply(this, args);
179
+ fire();
180
+ return r;
181
+ };
182
+ const ourReplace = function(...args) {
183
+ const r = origReplace.apply(this, args);
184
+ fire();
185
+ return r;
186
+ };
187
+ history.pushState = ourPush;
188
+ history.replaceState = ourReplace;
189
+ window.addEventListener("popstate", fire);
190
+ fire();
191
+ return () => {
192
+ stopped = true;
193
+ window.removeEventListener("popstate", fire);
194
+ if (history.pushState === ourPush) history.pushState = origPush;
195
+ if (history.replaceState === ourReplace) history.replaceState = origReplace;
196
+ };
197
+ }
198
+ var PLACEHOLDER, PATH_TOKEN, SENTINEL, TOKEN_RE, trimSlash;
199
+ var init_deeplink = __esm({
200
+ "src/deeplink.ts"() {
201
+ "use strict";
202
+ PLACEHOLDER = "{share_token}";
203
+ PATH_TOKEN = "{path}";
204
+ SENTINEL = "mf-share-token-sentinel";
205
+ TOKEN_RE = /^[A-Za-z0-9._~-]{4,256}$/;
206
+ trimSlash = (path) => path.length > 1 && path.endsWith("/") ? path.slice(0, -1) : path;
207
+ }
208
+ });
209
+
25
210
  // src/pageContextMode.ts
26
211
  function parsePageContextMode(value) {
27
212
  if (typeof value === "boolean") return value ? "full" : "off";
@@ -1618,7 +1803,7 @@ module.exports = __toCommonJS(react_exports);
1618
1803
  var import_react2 = require("react");
1619
1804
 
1620
1805
  // src/protocol.ts
1621
- var PROTOCOL_VERSION = 3;
1806
+ var PROTOCOL_VERSION = 4;
1622
1807
  var CHANNEL = "mf-embed";
1623
1808
  function envelope(payload, id) {
1624
1809
  return {
@@ -1840,6 +2025,7 @@ function dockBox(g, vw, _vh) {
1840
2025
  }
1841
2026
 
1842
2027
  // src/loader.ts
2028
+ var EMBED_VERSION = "0.18.0";
1843
2029
  function devRequested() {
1844
2030
  try {
1845
2031
  if (new URLSearchParams(location.search).get("mfdev") === "1") return true;
@@ -1927,6 +2113,8 @@ var EmbedHost = class {
1927
2113
  this.inline = !!config.container;
1928
2114
  }
1929
2115
  mount() {
2116
+ const w = window;
2117
+ (w.matterfact ?? (w.matterfact = {})).embedVersion = EMBED_VERSION;
1930
2118
  const host = document.createElement("div");
1931
2119
  this.hostEl = host;
1932
2120
  host.id = "matterfact-embed";
@@ -1970,7 +2158,7 @@ var EmbedHost = class {
1970
2158
  "sandbox",
1971
2159
  "allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox allow-downloads"
1972
2160
  );
1973
- iframe.setAttribute("allow", "microphone; clipboard-write");
2161
+ iframe.setAttribute("allow", "microphone; clipboard-write; web-share");
1974
2162
  iframe.src = `${this.config.origin}/embed/chat?k=${encodeURIComponent(
1975
2163
  this.config.publishableKey
1976
2164
  )}&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
@@ -1983,6 +2171,18 @@ var EmbedHost = class {
1983
2171
  window.addEventListener("resize", this.onViewportResize);
1984
2172
  this.place();
1985
2173
  }
2174
+ const fmt = this.config.shareDeeplinkFormat;
2175
+ if (fmt) {
2176
+ void Promise.resolve().then(() => (init_deeplink(), deeplink_exports)).then((m) => {
2177
+ if (this.destroyed) return;
2178
+ this.deeplinkStop = m.installDeeplinkWatch(
2179
+ fmt,
2180
+ (token) => this.send({ type: "host.openShare", token }),
2181
+ (format) => this.send({ type: "host.deeplinkFormat", format })
2182
+ );
2183
+ }).catch(() => {
2184
+ });
2185
+ }
1986
2186
  }
1987
2187
  /**
1988
2188
  * Test seam. The shadow root is CLOSED, so a test cannot reach the iframe to forge a
@@ -2406,6 +2606,9 @@ var EmbedHost = class {
2406
2606
  this.iframe = null;
2407
2607
  this.shadow = null;
2408
2608
  this.ready = false;
2609
+ this.destroyed = true;
2610
+ this.deeplinkStop?.();
2611
+ this.deeplinkStop = null;
2409
2612
  void this.context?.then((m) => m.stop());
2410
2613
  }
2411
2614
  };
@@ -2518,10 +2721,32 @@ function toolDescriptorKey(artifacts, resolve, tools) {
2518
2721
  }))
2519
2722
  });
2520
2723
  }
2724
+ var KNOWN_AGENT_PROPS = [
2725
+ "publishableKey",
2726
+ "widgetOrigin",
2727
+ "surface",
2728
+ "shareDeeplinkFormat",
2729
+ "theme",
2730
+ "getAuthToken",
2731
+ "getPageContext",
2732
+ "inline",
2733
+ "className",
2734
+ "style",
2735
+ "pageContext",
2736
+ "dev",
2737
+ "actions",
2738
+ "sitemap",
2739
+ "artifacts",
2740
+ "resolve",
2741
+ "tools",
2742
+ "onToolEvent",
2743
+ "onEvent"
2744
+ ];
2521
2745
  function MatterfactAgent({
2522
2746
  publishableKey: publishableKeyProp,
2523
2747
  widgetOrigin: widgetOriginProp,
2524
2748
  surface = "",
2749
+ shareDeeplinkFormat,
2525
2750
  theme: themeProp,
2526
2751
  getAuthToken: getAuthTokenProp,
2527
2752
  getPageContext,
@@ -2536,8 +2761,20 @@ function MatterfactAgent({
2536
2761
  resolve,
2537
2762
  tools,
2538
2763
  onToolEvent,
2539
- onEvent
2764
+ onEvent,
2765
+ ...unknownProps
2540
2766
  }) {
2767
+ (0, import_react2.useEffect)(() => {
2768
+ if (process.env.NODE_ENV === "production") return;
2769
+ for (const key of Object.keys(unknownProps)) {
2770
+ const meant = KNOWN_AGENT_PROPS.find(
2771
+ (k) => k.toLowerCase() === key.toLowerCase()
2772
+ );
2773
+ console.warn(
2774
+ `[mf-embed] unknown prop "${key}" on <MatterfactAgent>` + (meant ? ` \u2014 did you mean "${meant}"?` : "")
2775
+ );
2776
+ }
2777
+ }, []);
2541
2778
  const ctx = useMatterfactConfig();
2542
2779
  const publishableKey = publishableKeyProp ?? ctx?.publishableKey;
2543
2780
  if (!publishableKey) {
@@ -2583,7 +2820,8 @@ function MatterfactAgent({
2583
2820
  pageContextProvider: async () => await pageContextRef.current?.() ?? null,
2584
2821
  container: inline ? slot.current : null,
2585
2822
  pageContext,
2586
- dev
2823
+ dev,
2824
+ shareDeeplinkFormat
2587
2825
  };
2588
2826
  let host = null;
2589
2827
  try {
@@ -2603,6 +2841,7 @@ function MatterfactAgent({
2603
2841
  publishableKey,
2604
2842
  widgetOrigin2,
2605
2843
  surface,
2844
+ shareDeeplinkFormat,
2606
2845
  theme,
2607
2846
  inline,
2608
2847
  pageContext,
@@ -2713,11 +2952,11 @@ function MatterfactArtifact({
2713
2952
  post(changed);
2714
2953
  }, [paramsKey, post]);
2715
2954
  let src;
2716
- if (token) {
2717
- src = `${origin}/embed/artifacts/${encodeURIComponent(artifactName)}?owner=${encodeURIComponent(owner)}&t=${encodeURIComponent(token)}&theme=${theme}`;
2718
- } else if (ctx) {
2955
+ if (ctx) {
2719
2956
  const hostOrigin = typeof window === "undefined" ? "" : window.location.origin;
2720
2957
  src = `${origin}/embed/artifacts/${encodeURIComponent(artifactName)}?owner=${encodeURIComponent(owner)}&k=${encodeURIComponent(ctx.publishableKey)}&o=${encodeURIComponent(hostOrigin)}&theme=${theme}`;
2958
+ } else if (token) {
2959
+ src = `${origin}/embed/artifacts/${encodeURIComponent(artifactName)}?owner=${encodeURIComponent(owner)}&t=${encodeURIComponent(token)}&theme=${theme}`;
2721
2960
  } else {
2722
2961
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2723
2962
  "div",