@matterfact/embed 0.9.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 +119 -0
  2. package/dist/{chunk-URGQBG4I.js → chunk-4AE5WINC.js} +209 -17
  3. package/dist/chunk-4AE5WINC.js.map +1 -0
  4. package/dist/{chunk-6EM7T2JV.js → chunk-CFDPCZO3.js} +208 -15
  5. package/dist/chunk-CFDPCZO3.js.map +1 -0
  6. package/dist/{chunk-BKKXHSYU.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-U2HJJN2S.js → context-HOOW63MO.js} +4 -2
  11. package/dist/context-SSQ4HUP3.js +3 -0
  12. package/dist/{context-ACFBWIFH.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 +214 -13
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +33 -1
  18. package/dist/index.d.ts +33 -1
  19. package/dist/index.js +1 -1
  20. package/dist/index.js.map +1 -1
  21. package/dist/react.cjs +260 -15
  22. package/dist/react.cjs.map +1 -1
  23. package/dist/react.d.cts +124 -1
  24. package/dist/react.d.ts +124 -1
  25. package/dist/react.js +46 -3
  26. package/dist/react.js.map +1 -1
  27. package/dist/{snapshot-Y75SCGCM.js → snapshot-CRY2IBX6.js} +3 -3
  28. package/dist/{snapshot-Y75SCGCM.js.map → snapshot-CRY2IBX6.js.map} +1 -1
  29. package/dist/{snapshot-4GXT6PKZ.js → snapshot-EYRXPLRC.js} +2 -2
  30. package/package.json +1 -1
  31. package/dist/chunk-6EM7T2JV.js.map +0 -1
  32. package/dist/chunk-FEXG4LQJ.js +0 -3
  33. package/dist/chunk-FEXG4LQJ.js.map +0 -7
  34. package/dist/chunk-NWNMS34P.js +0 -2
  35. package/dist/chunk-URGQBG4I.js.map +0 -1
  36. package/dist/context-ACFBWIFH.js +0 -3
  37. /package/dist/{chunk-BKKXHSYU.js.map → chunk-DXLHTFB4.js.map} +0 -0
  38. /package/dist/{chunk-NWNMS34P.js.map → chunk-RL6VIGWK.js.map} +0 -0
  39. /package/dist/{context-U2HJJN2S.js.map → context-HOOW63MO.js.map} +0 -0
  40. /package/dist/{snapshot-4GXT6PKZ.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();
@@ -900,6 +1050,7 @@ __export(context_exports, {
900
1050
  isPrivate: () => isPrivate,
901
1051
  navigateHost: () => navigateHost,
902
1052
  provideContext: () => provideContext,
1053
+ readDeclaredGrants: () => readDeclaredGrants,
903
1054
  readPageContext: () => readPageContext,
904
1055
  redact: () => redact,
905
1056
  sendRegion: () => sendRegion,
@@ -1021,12 +1172,31 @@ function grantsFromIframeSrcs(srcs, origin) {
1021
1172
  }
1022
1173
  return out;
1023
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
+ }
1024
1189
  function readArtifactGrants() {
1025
1190
  if (!widgetOrigin) return [];
1026
1191
  const srcs = Array.from(document.querySelectorAll("iframe")).map(
1027
1192
  (f) => f.getAttribute("src") || ""
1028
1193
  );
1029
- 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;
1030
1200
  }
1031
1201
  function publishArtifactGrants() {
1032
1202
  send?.({ type: "host.artifactGrants", grants: readArtifactGrants() });
@@ -1115,10 +1285,7 @@ function watchNavigation() {
1115
1285
  void publishContext();
1116
1286
  publishArtifactGrants();
1117
1287
  publishSitemap();
1118
- send?.({
1119
- type: "host.tools",
1120
- tools: advertiseTools(typeof window !== "undefined" ? window : void 0)
1121
- });
1288
+ publishTools();
1122
1289
  };
1123
1290
  navFire = fire;
1124
1291
  for (const name of ["pushState", "replaceState"]) {
@@ -1223,10 +1390,24 @@ async function callTool(call, emit) {
1223
1390
  });
1224
1391
  return;
1225
1392
  }
1226
- const r = executeHostTool(
1227
- call,
1228
- typeof window !== "undefined" ? window : void 0
1229
- );
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
+ });
1230
1411
  emit({
1231
1412
  type: "host.toolResult",
1232
1413
  callId: call.callId,
@@ -1304,10 +1485,30 @@ function start(emit, origin, pageContext = true, provider) {
1304
1485
  const theme = matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
1305
1486
  emit({ type: "host.theme", mode: theme });
1306
1487
  if (pageContextOn) {
1307
- const tools = advertiseTools(
1308
- typeof window !== "undefined" ? window : void 0
1309
- );
1310
- 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
+ });
1311
1512
  }
1312
1513
  }
1313
1514
  var widgetOrigin, MAX_ACTIVITY, send, activitySeq, activity, lastUrl, pageContextOn, contextProvider, navFire, origPushState, origReplaceState, snapshotModule, FORM_CONTROLS, ACTIONABLE, lastFocus, focusTimer, snapshotSeq;
@@ -1317,6 +1518,7 @@ var init_context = __esm({
1317
1518
  init_redact();
1318
1519
  init_hoist();
1319
1520
  init_hoist_runtime();
1521
+ init_registry();
1320
1522
  init_redact();
1321
1523
  widgetOrigin = "";
1322
1524
  MAX_ACTIVITY = 40;
@@ -2089,6 +2291,36 @@ function writeSitemapGlobal(sitemap) {
2089
2291
  const w = window;
2090
2292
  (w.matterfact ?? (w.matterfact = {})).sitemap = sitemap;
2091
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
+ }
2092
2324
  function MatterfactAgent({
2093
2325
  publishableKey,
2094
2326
  widgetOrigin: widgetOrigin2,
@@ -2102,7 +2334,11 @@ function MatterfactAgent({
2102
2334
  pageContext,
2103
2335
  dev,
2104
2336
  actions,
2105
- sitemap
2337
+ sitemap,
2338
+ artifacts,
2339
+ resolve,
2340
+ tools,
2341
+ onToolEvent
2106
2342
  }) {
2107
2343
  const authRef = (0, import_react.useRef)(getAuthToken);
2108
2344
  authRef.current = getAuthToken;
@@ -2116,10 +2352,15 @@ function MatterfactAgent({
2116
2352
  (0, import_react.useEffect)(() => {
2117
2353
  writeSitemapGlobal(sitemap);
2118
2354
  }, [sitemapKey]);
2355
+ (0, import_react.useEffect)(() => {
2356
+ writeToolGlobals(artifacts, resolve, tools, onToolEvent);
2357
+ });
2358
+ const toolsKey = toolDescriptorKey(artifacts, resolve, tools);
2119
2359
  (0, import_react.useEffect)(() => {
2120
2360
  if (typeof window === "undefined") return;
2121
2361
  if (inline && !slot.current) return;
2122
2362
  writeActionsGlobal(actionsRef.current);
2363
+ writeToolGlobals(artifacts, resolve, tools, onToolEvent);
2123
2364
  const config = {
2124
2365
  publishableKey,
2125
2366
  origin: widgetOrigin2 || DEFAULT_ORIGIN,
@@ -2150,7 +2391,11 @@ function MatterfactAgent({
2150
2391
  inline,
2151
2392
  pageContext,
2152
2393
  dev,
2153
- 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
2154
2399
  ]);
2155
2400
  if (!inline) return null;
2156
2401
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(