@matterfact/embed 0.8.0 → 0.10.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 (40) hide show
  1. package/README.md +191 -0
  2. package/dist/{chunk-R2ZEJARX.js → chunk-4AE5WINC.js} +233 -17
  3. package/dist/chunk-4AE5WINC.js.map +1 -0
  4. package/dist/{chunk-PNSYFXXU.js → chunk-CFDPCZO3.js} +233 -15
  5. package/dist/chunk-CFDPCZO3.js.map +1 -0
  6. package/dist/{chunk-UD7CAQXV.js → chunk-DXLHTFB4.js} +2 -2
  7. package/dist/chunk-RL6VIGWK.js +2 -0
  8. package/dist/chunk-TWMHQF7O.js +3 -0
  9. package/dist/chunk-TWMHQF7O.js.map +7 -0
  10. package/dist/{context-MVGSYIMB.js → context-HOOW63MO.js} +6 -2
  11. package/dist/context-SSQ4HUP3.js +3 -0
  12. package/dist/{context-ARBB2XD6.js.map → context-SSQ4HUP3.js.map} +1 -1
  13. package/dist/embed.js +1 -1
  14. package/dist/embed.js.map +2 -2
  15. package/dist/index.cjs +243 -14
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +92 -2
  18. package/dist/index.d.ts +92 -2
  19. package/dist/index.js +5 -2
  20. package/dist/index.js.map +1 -1
  21. package/dist/react.cjs +299 -16
  22. package/dist/react.cjs.map +1 -1
  23. package/dist/react.d.cts +172 -1
  24. package/dist/react.d.ts +172 -1
  25. package/dist/react.js +60 -4
  26. package/dist/react.js.map +1 -1
  27. package/dist/{snapshot-GL4YBMXD.js → snapshot-CRY2IBX6.js} +3 -3
  28. package/dist/{snapshot-GL4YBMXD.js.map → snapshot-CRY2IBX6.js.map} +1 -1
  29. package/dist/{snapshot-UGTXZVB6.js → snapshot-EYRXPLRC.js} +2 -2
  30. package/package.json +1 -1
  31. package/dist/chunk-PNSYFXXU.js.map +0 -1
  32. package/dist/chunk-R2ZEJARX.js.map +0 -1
  33. package/dist/chunk-UQCETVRF.js +0 -2
  34. package/dist/chunk-W52Q7G4J.js +0 -3
  35. package/dist/chunk-W52Q7G4J.js.map +0 -7
  36. package/dist/context-ARBB2XD6.js +0 -3
  37. /package/dist/{chunk-UD7CAQXV.js.map → chunk-DXLHTFB4.js.map} +0 -0
  38. /package/dist/{chunk-UQCETVRF.js.map → chunk-RL6VIGWK.js.map} +0 -0
  39. /package/dist/{context-MVGSYIMB.js.map → context-HOOW63MO.js.map} +0 -0
  40. /package/dist/{snapshot-UGTXZVB6.js.map → snapshot-EYRXPLRC.js.map} +0 -0
package/dist/react.cjs CHANGED
@@ -417,6 +417,156 @@ var init_hoist_runtime = __esm({
417
417
  }
418
418
  });
419
419
 
420
+ // src/adapters/registry.ts
421
+ function mfGlobal(win) {
422
+ return win?.matterfact ?? {};
423
+ }
424
+ function toolClass(name) {
425
+ if (name.startsWith("hoist.")) return "hoist";
426
+ if (name.startsWith("mf.")) return "mf";
427
+ return "app";
428
+ }
429
+ function emitToolEvent(win, e) {
430
+ const cb = mfGlobal(win).onToolEvent;
431
+ if (typeof cb !== "function") return;
432
+ try {
433
+ cb(e);
434
+ } catch {
435
+ }
436
+ }
437
+ function mfTools(win) {
438
+ const { document: document2, artifact } = mfGlobal(win).resolve ?? {};
439
+ const out = [];
440
+ if (typeof document2 === "function") {
441
+ out.push({
442
+ name: "mf.resolveDocument",
443
+ description: 'Resolve a matterfact document (dossier, report, briefing) that is NOT on the current page to a concrete id. Call it with the document type and a key (e.g. a ticker) when the site map shows the document lives on another route. Returns {id, label, href}. Cite the result inline as <MFRef kind="document" id="<id>" label="<label>" /> \u2014 it renders as a chip that opens the document in the side panel; href is its "open on page" deeplink.',
444
+ inputSchema: {
445
+ type: "object",
446
+ properties: {
447
+ doctype: { type: "string" },
448
+ key: { type: "string" }
449
+ },
450
+ required: ["doctype", "key"]
451
+ },
452
+ readOnly: true,
453
+ confirm: "auto"
454
+ });
455
+ }
456
+ if (typeof artifact === "function") {
457
+ out.push({
458
+ name: "mf.resolveArtifact",
459
+ description: 'Resolve a matterfact artifact (an interactive board) that is NOT co-embedded on the current page to a read grant. Call it with the artifact slug. Returns {id, owner, token, label}; the grant is applied for you. Cite the result inline as <MFRef kind="artifact" id="<id>" label="<label>" /> \u2014 it renders as a chip that opens the artifact in the side panel.',
460
+ inputSchema: {
461
+ type: "object",
462
+ properties: { slug: { type: "string" } },
463
+ required: ["slug"]
464
+ },
465
+ readOnly: true,
466
+ confirm: "auto"
467
+ });
468
+ }
469
+ return out;
470
+ }
471
+ function appName(name) {
472
+ return name.startsWith("app.") ? name : `app.${name}`;
473
+ }
474
+ function appTools(win) {
475
+ const defs = mfGlobal(win).tools;
476
+ if (!Array.isArray(defs)) return [];
477
+ const out = [];
478
+ for (const d of defs) {
479
+ const raw = (d?.name ?? "").trim();
480
+ if (!raw || typeof d.handler !== "function") continue;
481
+ const name = appName(raw);
482
+ if (!APP_NAME_RE.test(name)) continue;
483
+ out.push({
484
+ name,
485
+ description: String(d.description ?? `Run the host tool ${name}.`),
486
+ inputSchema: d.inputSchema ?? { type: "object", properties: {} },
487
+ readOnly: false,
488
+ confirm: d.confirm === "auto" ? "auto" : "required"
489
+ });
490
+ }
491
+ return out;
492
+ }
493
+ function advertise(win = typeof window !== "undefined" ? window : void 0) {
494
+ const merged = [];
495
+ const seen = /* @__PURE__ */ new Set();
496
+ const push = (tools) => {
497
+ for (const t of tools) {
498
+ if (seen.has(t.name)) continue;
499
+ seen.add(t.name);
500
+ merged.push(t);
501
+ }
502
+ };
503
+ try {
504
+ push(advertiseTools(win));
505
+ } catch {
506
+ }
507
+ try {
508
+ push(mfTools(win));
509
+ push(appTools(win));
510
+ } catch {
511
+ }
512
+ return merged.slice(0, MAX_TOOLS);
513
+ }
514
+ function timeout(ms) {
515
+ return new Promise(
516
+ (_, reject) => setTimeout(() => reject(new Error("host tool timed out")), ms)
517
+ );
518
+ }
519
+ function redactResult(value) {
520
+ if (value === void 0 || value === null) return value;
521
+ if (typeof value === "string") return redact(value);
522
+ try {
523
+ return JSON.parse(redact(JSON.stringify(value)));
524
+ } catch {
525
+ return redact(String(value));
526
+ }
527
+ }
528
+ async function execute(call, win = typeof window !== "undefined" ? window : void 0) {
529
+ const cls = toolClass(call.name);
530
+ try {
531
+ if (cls === "hoist") {
532
+ return executeHostTool(call, win);
533
+ }
534
+ const handler = resolveHandler(win, call.name);
535
+ if (!handler) return { ok: false, error: "unknown tool" };
536
+ const result = await Promise.race([
537
+ Promise.resolve(handler(call.args ?? {})),
538
+ timeout(HOST_HANDLER_TIMEOUT_MS)
539
+ ]);
540
+ return { ok: true, result: redactResult(result) };
541
+ } catch (e) {
542
+ return {
543
+ ok: false,
544
+ error: redact(e instanceof Error ? e.message : "host tool failed")
545
+ };
546
+ }
547
+ }
548
+ function resolveHandler(win, name) {
549
+ const mf = mfGlobal(win);
550
+ if (name === "mf.resolveDocument") return mf.resolve?.document;
551
+ if (name === "mf.resolveArtifact") return mf.resolve?.artifact;
552
+ if (name.startsWith("app.")) {
553
+ const def = (mf.tools ?? []).find((d) => appName((d?.name ?? "").trim()) === name);
554
+ return def?.handler;
555
+ }
556
+ return void 0;
557
+ }
558
+ var MAX_TOOLS, HOST_HANDLER_TIMEOUT_MS, APP_NAME_RE;
559
+ var init_registry = __esm({
560
+ "src/adapters/registry.ts"() {
561
+ "use strict";
562
+ init_redact();
563
+ init_hoist_runtime();
564
+ MAX_TOOLS = 16;
565
+ HOST_HANDLER_TIMEOUT_MS = 3e4;
566
+ APP_NAME_RE = /^[A-Za-z0-9._-]+$/;
567
+ }
568
+ });
569
+
420
570
  // src/dom/a11y.ts
421
571
  function createStyleCache() {
422
572
  const cache = /* @__PURE__ */ new WeakMap();
@@ -898,7 +1048,9 @@ __export(context_exports, {
898
1048
  callTool: () => callTool,
899
1049
  grantsFromIframeSrcs: () => grantsFromIframeSrcs,
900
1050
  isPrivate: () => isPrivate,
1051
+ navigateHost: () => navigateHost,
901
1052
  provideContext: () => provideContext,
1053
+ readDeclaredGrants: () => readDeclaredGrants,
902
1054
  readPageContext: () => readPageContext,
903
1055
  redact: () => redact,
904
1056
  sendRegion: () => sendRegion,
@@ -998,6 +1150,7 @@ async function publishContext() {
998
1150
  }
999
1151
  function provideContext() {
1000
1152
  void publishContext();
1153
+ publishSitemap();
1001
1154
  }
1002
1155
  function grantsFromIframeSrcs(srcs, origin) {
1003
1156
  const out = [];
@@ -1019,16 +1172,56 @@ function grantsFromIframeSrcs(srcs, origin) {
1019
1172
  }
1020
1173
  return out;
1021
1174
  }
1175
+ function readDeclaredGrants() {
1176
+ const declared = window.matterfact?.artifacts;
1177
+ if (!Array.isArray(declared)) return [];
1178
+ const out = [];
1179
+ for (const a of declared) {
1180
+ const id = (a?.slug || "").trim();
1181
+ const owner = (a?.owner || "").trim();
1182
+ const token = (a?.token || "").trim();
1183
+ if (!id || !owner || !token) continue;
1184
+ if (out.some((g) => g.id === id)) continue;
1185
+ out.push({ id, owner, token });
1186
+ }
1187
+ return out;
1188
+ }
1022
1189
  function readArtifactGrants() {
1023
1190
  if (!widgetOrigin) return [];
1024
1191
  const srcs = Array.from(document.querySelectorAll("iframe")).map(
1025
1192
  (f) => f.getAttribute("src") || ""
1026
1193
  );
1027
- return grantsFromIframeSrcs(srcs, widgetOrigin);
1194
+ const scanned = grantsFromIframeSrcs(srcs, widgetOrigin);
1195
+ const out = [...scanned];
1196
+ for (const g of readDeclaredGrants()) {
1197
+ if (!out.some((s) => s.id === g.id)) out.push(g);
1198
+ }
1199
+ return out;
1028
1200
  }
1029
1201
  function publishArtifactGrants() {
1030
1202
  send?.({ type: "host.artifactGrants", grants: readArtifactGrants() });
1031
1203
  }
1204
+ function readSitemap2() {
1205
+ const s = window.matterfact?.sitemap;
1206
+ return Array.isArray(s) ? s : [];
1207
+ }
1208
+ function publishSitemap() {
1209
+ if (!pageContextOn) return;
1210
+ const sitemap = readSitemap2();
1211
+ if (sitemap.length) send?.({ type: "host.sitemap", sitemap });
1212
+ }
1213
+ function navigateHost(href) {
1214
+ if (typeof href !== "string" || !href) return;
1215
+ let target;
1216
+ try {
1217
+ target = new URL(href, location.href);
1218
+ } catch {
1219
+ return;
1220
+ }
1221
+ if (target.protocol !== "http:" && target.protocol !== "https:") return;
1222
+ if (target.origin !== location.origin) return;
1223
+ location.assign(target.href);
1224
+ }
1032
1225
  function pushActivity(e) {
1033
1226
  activity.push({ ...e, seq: ++activitySeq, ts: Date.now() });
1034
1227
  if (activity.length > MAX_ACTIVITY) activity.shift();
@@ -1091,10 +1284,8 @@ function watchNavigation() {
1091
1284
  pushActivity({ type: "nav", summary: `navigated to ${url}` });
1092
1285
  void publishContext();
1093
1286
  publishArtifactGrants();
1094
- send?.({
1095
- type: "host.tools",
1096
- tools: advertiseTools(typeof window !== "undefined" ? window : void 0)
1097
- });
1287
+ publishSitemap();
1288
+ publishTools();
1098
1289
  };
1099
1290
  navFire = fire;
1100
1291
  for (const name of ["pushState", "replaceState"]) {
@@ -1199,10 +1390,24 @@ async function callTool(call, emit) {
1199
1390
  });
1200
1391
  return;
1201
1392
  }
1202
- const r = executeHostTool(
1203
- call,
1204
- typeof window !== "undefined" ? window : void 0
1205
- );
1393
+ const win = typeof window !== "undefined" ? window : void 0;
1394
+ const cls = toolClass(call.name);
1395
+ const startedAt = Date.now();
1396
+ emitToolEvent(win, {
1397
+ phase: "call",
1398
+ name: call.name,
1399
+ toolClass: cls,
1400
+ args: call.args
1401
+ });
1402
+ const r = await execute(call, win);
1403
+ emitToolEvent(win, {
1404
+ phase: "result",
1405
+ name: call.name,
1406
+ toolClass: cls,
1407
+ ok: r.ok,
1408
+ error: r.error,
1409
+ ms: Date.now() - startedAt
1410
+ });
1206
1411
  emit({
1207
1412
  type: "host.toolResult",
1208
1413
  callId: call.callId,
@@ -1251,6 +1456,7 @@ function start(emit, origin, pageContext = true, provider) {
1251
1456
  if (pageContextOn) {
1252
1457
  void publishContext();
1253
1458
  publishArtifactGrants();
1459
+ publishSitemap();
1254
1460
  watchNavigation();
1255
1461
  document.addEventListener("click", onClick, {
1256
1462
  capture: true,
@@ -1279,10 +1485,30 @@ function start(emit, origin, pageContext = true, provider) {
1279
1485
  const theme = matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
1280
1486
  emit({ type: "host.theme", mode: theme });
1281
1487
  if (pageContextOn) {
1282
- const tools = advertiseTools(
1283
- typeof window !== "undefined" ? window : void 0
1284
- );
1285
- if (tools.length) emit({ type: "host.tools", tools });
1488
+ const w = typeof window !== "undefined" ? window : void 0;
1489
+ const tools = advertise(w);
1490
+ if (tools.length) {
1491
+ emit({ type: "host.tools", tools });
1492
+ for (const t of tools) {
1493
+ emitToolEvent(w, {
1494
+ phase: "advertise",
1495
+ name: t.name,
1496
+ toolClass: toolClass(t.name)
1497
+ });
1498
+ }
1499
+ }
1500
+ }
1501
+ }
1502
+ function publishTools() {
1503
+ const win = typeof window !== "undefined" ? window : void 0;
1504
+ const tools = advertise(win);
1505
+ send?.({ type: "host.tools", tools });
1506
+ for (const t of tools) {
1507
+ emitToolEvent(win, {
1508
+ phase: "advertise",
1509
+ name: t.name,
1510
+ toolClass: toolClass(t.name)
1511
+ });
1286
1512
  }
1287
1513
  }
1288
1514
  var widgetOrigin, MAX_ACTIVITY, send, activitySeq, activity, lastUrl, pageContextOn, contextProvider, navFire, origPushState, origReplaceState, snapshotModule, FORM_CONTROLS, ACTIONABLE, lastFocus, focusTimer, snapshotSeq;
@@ -1292,6 +1518,7 @@ var init_context = __esm({
1292
1518
  init_redact();
1293
1519
  init_hoist();
1294
1520
  init_hoist_runtime();
1521
+ init_registry();
1295
1522
  init_redact();
1296
1523
  widgetOrigin = "";
1297
1524
  MAX_ACTIVITY = 40;
@@ -1322,7 +1549,7 @@ module.exports = __toCommonJS(react_exports);
1322
1549
  var import_react = require("react");
1323
1550
 
1324
1551
  // src/protocol.ts
1325
- var PROTOCOL_VERSION = 2;
1552
+ var PROTOCOL_VERSION = 3;
1326
1553
  var CHANNEL = "mf-embed";
1327
1554
  function envelope(payload, id) {
1328
1555
  return {
@@ -1892,6 +2119,9 @@ var EmbedHost = class {
1892
2119
  case "widget.needsAuth":
1893
2120
  void this.provideAuth();
1894
2121
  break;
2122
+ case "widget.navigate":
2123
+ void this.loadContext().then((m) => m.navigateHost(msg.href));
2124
+ break;
1895
2125
  }
1896
2126
  }
1897
2127
  /**
@@ -2056,6 +2286,41 @@ function writeActionsGlobal(actions) {
2056
2286
  const mf = w.matterfact ?? (w.matterfact = {});
2057
2287
  mf.hoist = { ...mf.hoist, actions: actions ?? { navigate: "off" } };
2058
2288
  }
2289
+ function writeSitemapGlobal(sitemap) {
2290
+ if (typeof window === "undefined" || sitemap === void 0) return;
2291
+ const w = window;
2292
+ (w.matterfact ?? (w.matterfact = {})).sitemap = sitemap;
2293
+ }
2294
+ function writeToolGlobals(artifacts, resolve, tools, onToolEvent) {
2295
+ if (typeof window === "undefined") return;
2296
+ const mf = window.matterfact ?? (window.matterfact = {});
2297
+ mf.artifacts = artifacts ?? [];
2298
+ mf.resolve = resolve ?? {};
2299
+ mf.tools = tools ?? [];
2300
+ mf.onToolEvent = onToolEvent;
2301
+ }
2302
+ function toolDescriptorKey(artifacts, resolve, tools) {
2303
+ return JSON.stringify({
2304
+ // Artifact identity is data (slug/owner/label), not a function.
2305
+ artifacts: (artifacts ?? []).map((a) => ({
2306
+ slug: a.slug,
2307
+ owner: a.owner,
2308
+ label: a.label
2309
+ })),
2310
+ // For resolvers, only WHICH contracts are present changes the advertised set.
2311
+ resolve: {
2312
+ document: typeof resolve?.document === "function",
2313
+ artifact: typeof resolve?.artifact === "function"
2314
+ },
2315
+ // For host tools, the advertised descriptor — never the handler.
2316
+ tools: (tools ?? []).map((t) => ({
2317
+ name: t.name,
2318
+ description: t.description,
2319
+ inputSchema: t.inputSchema,
2320
+ confirm: t.confirm
2321
+ }))
2322
+ });
2323
+ }
2059
2324
  function MatterfactAgent({
2060
2325
  publishableKey,
2061
2326
  widgetOrigin: widgetOrigin2,
@@ -2068,7 +2333,12 @@ function MatterfactAgent({
2068
2333
  style,
2069
2334
  pageContext,
2070
2335
  dev,
2071
- actions
2336
+ actions,
2337
+ sitemap,
2338
+ artifacts,
2339
+ resolve,
2340
+ tools,
2341
+ onToolEvent
2072
2342
  }) {
2073
2343
  const authRef = (0, import_react.useRef)(getAuthToken);
2074
2344
  authRef.current = getAuthToken;
@@ -2078,10 +2348,19 @@ function MatterfactAgent({
2078
2348
  actionsRef.current = actions;
2079
2349
  const actionsKey = JSON.stringify(actions ?? null);
2080
2350
  const slot = (0, import_react.useRef)(null);
2351
+ const sitemapKey = JSON.stringify(sitemap ?? null);
2352
+ (0, import_react.useEffect)(() => {
2353
+ writeSitemapGlobal(sitemap);
2354
+ }, [sitemapKey]);
2355
+ (0, import_react.useEffect)(() => {
2356
+ writeToolGlobals(artifacts, resolve, tools, onToolEvent);
2357
+ });
2358
+ const toolsKey = toolDescriptorKey(artifacts, resolve, tools);
2081
2359
  (0, import_react.useEffect)(() => {
2082
2360
  if (typeof window === "undefined") return;
2083
2361
  if (inline && !slot.current) return;
2084
2362
  writeActionsGlobal(actionsRef.current);
2363
+ writeToolGlobals(artifacts, resolve, tools, onToolEvent);
2085
2364
  const config = {
2086
2365
  publishableKey,
2087
2366
  origin: widgetOrigin2 || DEFAULT_ORIGIN,
@@ -2112,7 +2391,11 @@ function MatterfactAgent({
2112
2391
  inline,
2113
2392
  pageContext,
2114
2393
  dev,
2115
- actionsKey
2394
+ actionsKey,
2395
+ // A change to the advertised tool SHAPE remounts so start() re-advertises under it;
2396
+ // a change to only a handler body or callback identity does not (see toolDescriptorKey).
2397
+ toolsKey
2398
+ // eslint-disable-next-line react-hooks/exhaustive-deps
2116
2399
  ]);
2117
2400
  if (!inline) return null;
2118
2401
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(