@palettelab/sdk 0.1.20 → 0.1.22

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.js CHANGED
@@ -20,8 +20,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var src_exports = {};
22
22
  __export(src_exports, {
23
+ BrokerCallError: () => BrokerCallError,
24
+ CrossAppGrantError: () => CrossAppGrantError,
23
25
  DataRoomClient: () => DataRoomClient,
24
26
  Link: () => Link,
27
+ MissingDependencyError: () => MissingDependencyError,
25
28
  OrganizationClient: () => OrganizationClient,
26
29
  PaletteApiError: () => PaletteApiError,
27
30
  PaletteAppRouter: () => PaletteAppRouter,
@@ -31,12 +34,14 @@ __export(src_exports, {
31
34
  UserClient: () => UserClient,
32
35
  apiFetch: () => apiFetch,
33
36
  apiUpload: () => apiUpload,
37
+ broker: () => broker,
34
38
  createMockPlatformContext: () => createMockPlatformContext,
35
39
  createPaletteClient: () => createPaletteClient,
36
40
  createSandboxBridge: () => createSandboxBridge,
37
41
  dataRooms: () => dataRooms,
38
42
  disconnectConnection: () => disconnectConnection,
39
43
  errorFromResponse: () => errorFromResponse,
44
+ events: () => events,
40
45
  getBaseUrl: () => getBaseUrl,
41
46
  getConnections: () => getConnections,
42
47
  getInstallConfig: () => getInstallConfig,
@@ -47,7 +52,9 @@ __export(src_exports, {
47
52
  isSandboxRuntime: () => isSandboxRuntime,
48
53
  normalizePaletteLanguage: () => normalizePaletteLanguage,
49
54
  notFound: () => notFound,
55
+ palette: () => palette,
50
56
  requireConnection: () => requireConnection,
57
+ servicesProxy: () => servicesProxy,
51
58
  setBaseUrl: () => setBaseUrl,
52
59
  startConnection: () => startConnection,
53
60
  translate: () => translate,
@@ -385,6 +392,272 @@ var DataRoomClient = class {
385
392
  };
386
393
  var dataRooms = new DataRoomClient();
387
394
 
395
+ // src/services.ts
396
+ var BrokerCallError = class extends Error {
397
+ constructor(message, target, status) {
398
+ super(message);
399
+ this.target = target;
400
+ this.status = status;
401
+ this.name = "BrokerCallError";
402
+ }
403
+ };
404
+ var MissingDependencyError = class extends BrokerCallError {
405
+ constructor(message, target, status) {
406
+ super(message, target, status);
407
+ this.name = "MissingDependencyError";
408
+ }
409
+ };
410
+ var CrossAppGrantError = class extends BrokerCallError {
411
+ constructor(message, target, status) {
412
+ super(message, target, status);
413
+ this.name = "CrossAppGrantError";
414
+ }
415
+ };
416
+ function brokerError(message, target, status) {
417
+ const lower = message.toLowerCase();
418
+ if (status === 424 || lower.includes("not installed") || lower.includes("provider app")) {
419
+ return new MissingDependencyError(message, target, status);
420
+ }
421
+ if (status === 403 && (lower.includes("grant") || lower.includes("declared"))) {
422
+ return new CrossAppGrantError(message, target, status);
423
+ }
424
+ return new BrokerCallError(message, target, status);
425
+ }
426
+ var CALLER_HEADER = "X-Palette-Caller-App";
427
+ function currentPluginId() {
428
+ if (typeof window === "undefined") return void 0;
429
+ const match = window.location.pathname.match(/\/apps\/([^/?#]+)/);
430
+ return match ? decodeURIComponent(match[1]) : void 0;
431
+ }
432
+ async function brokerCallDirect(target, payload, options = {}) {
433
+ const callerAppId = options.callerAppId ?? currentPluginId();
434
+ const headers = {};
435
+ if (callerAppId) headers[CALLER_HEADER] = callerAppId;
436
+ let res;
437
+ try {
438
+ res = await apiFetch("/api/v1/os-broker/dispatch", {
439
+ method: "POST",
440
+ headers,
441
+ body: JSON.stringify({ target, payload: payload ?? {}, caller_app_id: callerAppId }),
442
+ signal: options.signal
443
+ });
444
+ } catch (error) {
445
+ const e = error;
446
+ throw brokerError(typeof e.detail === "string" ? e.detail : e.message, target, e.status);
447
+ }
448
+ if (!res.ok) {
449
+ let detail;
450
+ try {
451
+ detail = (await res.json()).detail;
452
+ } catch {
453
+ detail = res.statusText;
454
+ }
455
+ throw brokerError(detail ?? `broker dispatch failed: ${res.status}`, target, res.status);
456
+ }
457
+ const body = await res.json();
458
+ return body.result;
459
+ }
460
+ async function brokerCallSandboxed(target, payload, options = {}) {
461
+ return sandboxRequest({
462
+ kind: "palette.broker.call",
463
+ target,
464
+ payload: payload ?? {},
465
+ callerAppId: options.callerAppId
466
+ });
467
+ }
468
+ async function brokerEmitDirect(target, payload, options = {}) {
469
+ const callerAppId = options.callerAppId ?? currentPluginId();
470
+ if (!callerAppId) {
471
+ throw new BrokerCallError(
472
+ "events.emit requires a caller app id \u2014 only plugins may emit declared events",
473
+ target
474
+ );
475
+ }
476
+ const res = await apiFetch("/api/v1/os-broker/events/emit", {
477
+ method: "POST",
478
+ headers: { [CALLER_HEADER]: callerAppId },
479
+ body: JSON.stringify({ target, payload: payload ?? {}, caller_app_id: callerAppId }),
480
+ signal: options.signal
481
+ });
482
+ if (!res.ok) {
483
+ let detail;
484
+ try {
485
+ detail = (await res.json()).detail;
486
+ } catch {
487
+ detail = res.statusText;
488
+ }
489
+ throw new BrokerCallError(detail ?? `event emit failed: ${res.status}`, target, res.status);
490
+ }
491
+ }
492
+ async function brokerEmitSandboxed(target, payload, options = {}) {
493
+ await sandboxRequest({
494
+ kind: "palette.broker.emit",
495
+ target,
496
+ payload: payload ?? {},
497
+ callerAppId: options.callerAppId
498
+ });
499
+ }
500
+ function sandboxRequest(message) {
501
+ if (typeof window === "undefined") {
502
+ return Promise.reject(new BrokerCallError("sandbox bridge unavailable", message.target));
503
+ }
504
+ return new Promise((resolve, reject) => {
505
+ const id = `${Date.now()}.${Math.random().toString(36).slice(2)}`;
506
+ const onMessage = (ev) => {
507
+ const data = ev.data;
508
+ if (!data || data.id !== id) return;
509
+ window.removeEventListener("message", onMessage);
510
+ if (data.error) {
511
+ reject(new BrokerCallError(data.error.message, message.target, data.error.status));
512
+ } else {
513
+ resolve(data.result);
514
+ }
515
+ };
516
+ window.addEventListener("message", onMessage);
517
+ window.parent?.postMessage({ ...message, id }, "*");
518
+ });
519
+ }
520
+ var broker = {
521
+ async call(target, payload, options) {
522
+ return isSandboxRuntime() ? brokerCallSandboxed(target, payload, options) : brokerCallDirect(target, payload, options);
523
+ },
524
+ async emit(target, payload, options) {
525
+ if (isSandboxRuntime()) return brokerEmitSandboxed(target, payload, options);
526
+ return brokerEmitDirect(target, payload, options);
527
+ }
528
+ };
529
+ function servicesProxy(namespaceVersion) {
530
+ if (!namespaceVersion.includes("/")) {
531
+ throw new BrokerCallError("namespaceVersion must look like 'org/v1'", namespaceVersion);
532
+ }
533
+ const buildProxy = (segments) => new Proxy(function() {
534
+ }, {
535
+ get(_target, prop) {
536
+ if (typeof prop !== "string") return void 0;
537
+ return buildProxy([...segments, prop]);
538
+ },
539
+ apply(_target, _thisArg, args) {
540
+ const method = segments.join(".");
541
+ if (!method) {
542
+ throw new BrokerCallError("missing method name", namespaceVersion);
543
+ }
544
+ const [payload, options] = args;
545
+ return broker.call(`${namespaceVersion}#${method}`, payload, options);
546
+ }
547
+ });
548
+ return buildProxy([]);
549
+ }
550
+
551
+ // src/events.ts
552
+ var SUBSCRIBERS = /* @__PURE__ */ new Map();
553
+ var activeStream = null;
554
+ var activeStreamUrl = null;
555
+ var sandboxBound = false;
556
+ function currentPluginId2() {
557
+ if (typeof window === "undefined") return void 0;
558
+ const match = window.location.pathname.match(/\/apps\/([^/?#]+)/);
559
+ return match ? decodeURIComponent(match[1]) : void 0;
560
+ }
561
+ function streamUrl() {
562
+ const params = new URLSearchParams();
563
+ const targets = Array.from(SUBSCRIBERS.keys());
564
+ if (targets.length) params.set("targets", targets.join(","));
565
+ const callerAppId = currentPluginId2();
566
+ if (callerAppId) params.set("caller_app_id", callerAppId);
567
+ const query = params.toString();
568
+ return `${getBaseUrl()}/api/v1/os-broker/events/stream${query ? `?${query}` : ""}`;
569
+ }
570
+ function ensureStream() {
571
+ if (activeStream || typeof window === "undefined") return;
572
+ activeStreamUrl = streamUrl();
573
+ activeStream = new EventSource(activeStreamUrl, { withCredentials: true });
574
+ activeStream.onmessage = (ev) => {
575
+ try {
576
+ const data = JSON.parse(ev.data);
577
+ const handlers = SUBSCRIBERS.get(data.target);
578
+ if (!handlers) return;
579
+ for (const handler of handlers) {
580
+ try {
581
+ handler(data.payload, data.meta);
582
+ } catch (err) {
583
+ console.error("palette.events handler threw", err);
584
+ }
585
+ }
586
+ } catch (err) {
587
+ console.warn("palette.events: malformed event", err);
588
+ }
589
+ };
590
+ activeStream.onerror = () => {
591
+ };
592
+ }
593
+ function refreshStream() {
594
+ if (typeof window === "undefined" || isSandboxRuntime()) return;
595
+ const nextUrl = streamUrl();
596
+ if (activeStream && activeStreamUrl === nextUrl) return;
597
+ activeStream?.close();
598
+ activeStream = null;
599
+ activeStreamUrl = null;
600
+ if (SUBSCRIBERS.size > 0) ensureStream();
601
+ }
602
+ function ensureSandboxBound() {
603
+ if (sandboxBound || typeof window === "undefined") return;
604
+ sandboxBound = true;
605
+ window.addEventListener("message", (ev) => {
606
+ const data = ev.data;
607
+ if (!data || data.kind !== "palette.broker.event" || !data.target) return;
608
+ const handlers = SUBSCRIBERS.get(data.target);
609
+ if (!handlers) return;
610
+ const meta = data.meta ?? { target: data.target, organizationId: null, occurredAt: (/* @__PURE__ */ new Date()).toISOString() };
611
+ for (const handler of handlers) {
612
+ try {
613
+ handler(data.payload, meta);
614
+ } catch (err) {
615
+ console.error("palette.events handler threw", err);
616
+ }
617
+ }
618
+ });
619
+ }
620
+ var events = {
621
+ on(target, handler) {
622
+ let handlers = SUBSCRIBERS.get(target);
623
+ if (!handlers) {
624
+ handlers = /* @__PURE__ */ new Set();
625
+ SUBSCRIBERS.set(target, handlers);
626
+ }
627
+ handlers.add(handler);
628
+ if (isSandboxRuntime()) {
629
+ ensureSandboxBound();
630
+ window.parent?.postMessage({ kind: "palette.broker.subscribe", target }, "*");
631
+ } else {
632
+ refreshStream();
633
+ }
634
+ return () => {
635
+ handlers.delete(handler);
636
+ if (handlers.size === 0) {
637
+ SUBSCRIBERS.delete(target);
638
+ if (isSandboxRuntime()) {
639
+ window.parent?.postMessage({ kind: "palette.broker.unsubscribe", target }, "*");
640
+ } else {
641
+ refreshStream();
642
+ }
643
+ }
644
+ };
645
+ },
646
+ async emit(target, payload) {
647
+ await broker.emit(target, payload);
648
+ },
649
+ async publishDurable(target, payload) {
650
+ await broker.emit(target, payload);
651
+ },
652
+ async emitDurable(target, payload) {
653
+ await broker.emit(target, payload);
654
+ },
655
+ /** Diagnostic: list active subscriptions in this page. */
656
+ active() {
657
+ return Array.from(SUBSCRIBERS.keys());
658
+ }
659
+ };
660
+
388
661
  // src/permissions.ts
389
662
  function hasPermission(ctx, permission) {
390
663
  return ctx.permissions?.includes(permission) ?? false;
@@ -714,9 +987,21 @@ function createPaletteClient(ctx) {
714
987
  success: (message) => ctx?.showToast(message, "success"),
715
988
  error: (message) => ctx?.showToast(message, "error"),
716
989
  info: (message) => ctx?.showToast(message, "info")
717
- }
990
+ },
991
+ /**
992
+ * OS-broker RPC. Typed entry point: `palette.services("org/v1").members.list({...})`.
993
+ * The proxy resolves a dotted method chain into a qualified target string
994
+ * and dispatches via the broker (in-process for plugins on the host,
995
+ * postMessage for sandboxed iframe apps).
996
+ */
997
+ services: (namespaceVersion) => servicesProxy(namespaceVersion),
998
+ /** Low-level broker accessor when you don't want the typed proxy. */
999
+ broker,
1000
+ /** OS-broker pub/sub. `events.on(target, fn)` returns an unsubscribe function. */
1001
+ events
718
1002
  };
719
1003
  }
1004
+ var palette = createPaletteClient();
720
1005
 
721
1006
  // src/i18n.ts
722
1007
  var import_react3 = require("react");
@@ -1179,8 +1464,11 @@ function usePluginChat(agentId) {
1179
1464
  }
1180
1465
  // Annotate the CommonJS export names for ESM import in node:
1181
1466
  0 && (module.exports = {
1467
+ BrokerCallError,
1468
+ CrossAppGrantError,
1182
1469
  DataRoomClient,
1183
1470
  Link,
1471
+ MissingDependencyError,
1184
1472
  OrganizationClient,
1185
1473
  PaletteApiError,
1186
1474
  PaletteAppRouter,
@@ -1190,12 +1478,14 @@ function usePluginChat(agentId) {
1190
1478
  UserClient,
1191
1479
  apiFetch,
1192
1480
  apiUpload,
1481
+ broker,
1193
1482
  createMockPlatformContext,
1194
1483
  createPaletteClient,
1195
1484
  createSandboxBridge,
1196
1485
  dataRooms,
1197
1486
  disconnectConnection,
1198
1487
  errorFromResponse,
1488
+ events,
1199
1489
  getBaseUrl,
1200
1490
  getConnections,
1201
1491
  getInstallConfig,
@@ -1206,7 +1496,9 @@ function usePluginChat(agentId) {
1206
1496
  isSandboxRuntime,
1207
1497
  normalizePaletteLanguage,
1208
1498
  notFound,
1499
+ palette,
1209
1500
  requireConnection,
1501
+ servicesProxy,
1210
1502
  setBaseUrl,
1211
1503
  startConnection,
1212
1504
  translate,
package/dist/index.mjs CHANGED
@@ -317,6 +317,272 @@ var DataRoomClient = class {
317
317
  };
318
318
  var dataRooms = new DataRoomClient();
319
319
 
320
+ // src/services.ts
321
+ var BrokerCallError = class extends Error {
322
+ constructor(message, target, status) {
323
+ super(message);
324
+ this.target = target;
325
+ this.status = status;
326
+ this.name = "BrokerCallError";
327
+ }
328
+ };
329
+ var MissingDependencyError = class extends BrokerCallError {
330
+ constructor(message, target, status) {
331
+ super(message, target, status);
332
+ this.name = "MissingDependencyError";
333
+ }
334
+ };
335
+ var CrossAppGrantError = class extends BrokerCallError {
336
+ constructor(message, target, status) {
337
+ super(message, target, status);
338
+ this.name = "CrossAppGrantError";
339
+ }
340
+ };
341
+ function brokerError(message, target, status) {
342
+ const lower = message.toLowerCase();
343
+ if (status === 424 || lower.includes("not installed") || lower.includes("provider app")) {
344
+ return new MissingDependencyError(message, target, status);
345
+ }
346
+ if (status === 403 && (lower.includes("grant") || lower.includes("declared"))) {
347
+ return new CrossAppGrantError(message, target, status);
348
+ }
349
+ return new BrokerCallError(message, target, status);
350
+ }
351
+ var CALLER_HEADER = "X-Palette-Caller-App";
352
+ function currentPluginId() {
353
+ if (typeof window === "undefined") return void 0;
354
+ const match = window.location.pathname.match(/\/apps\/([^/?#]+)/);
355
+ return match ? decodeURIComponent(match[1]) : void 0;
356
+ }
357
+ async function brokerCallDirect(target, payload, options = {}) {
358
+ const callerAppId = options.callerAppId ?? currentPluginId();
359
+ const headers = {};
360
+ if (callerAppId) headers[CALLER_HEADER] = callerAppId;
361
+ let res;
362
+ try {
363
+ res = await apiFetch("/api/v1/os-broker/dispatch", {
364
+ method: "POST",
365
+ headers,
366
+ body: JSON.stringify({ target, payload: payload ?? {}, caller_app_id: callerAppId }),
367
+ signal: options.signal
368
+ });
369
+ } catch (error) {
370
+ const e = error;
371
+ throw brokerError(typeof e.detail === "string" ? e.detail : e.message, target, e.status);
372
+ }
373
+ if (!res.ok) {
374
+ let detail;
375
+ try {
376
+ detail = (await res.json()).detail;
377
+ } catch {
378
+ detail = res.statusText;
379
+ }
380
+ throw brokerError(detail ?? `broker dispatch failed: ${res.status}`, target, res.status);
381
+ }
382
+ const body = await res.json();
383
+ return body.result;
384
+ }
385
+ async function brokerCallSandboxed(target, payload, options = {}) {
386
+ return sandboxRequest({
387
+ kind: "palette.broker.call",
388
+ target,
389
+ payload: payload ?? {},
390
+ callerAppId: options.callerAppId
391
+ });
392
+ }
393
+ async function brokerEmitDirect(target, payload, options = {}) {
394
+ const callerAppId = options.callerAppId ?? currentPluginId();
395
+ if (!callerAppId) {
396
+ throw new BrokerCallError(
397
+ "events.emit requires a caller app id \u2014 only plugins may emit declared events",
398
+ target
399
+ );
400
+ }
401
+ const res = await apiFetch("/api/v1/os-broker/events/emit", {
402
+ method: "POST",
403
+ headers: { [CALLER_HEADER]: callerAppId },
404
+ body: JSON.stringify({ target, payload: payload ?? {}, caller_app_id: callerAppId }),
405
+ signal: options.signal
406
+ });
407
+ if (!res.ok) {
408
+ let detail;
409
+ try {
410
+ detail = (await res.json()).detail;
411
+ } catch {
412
+ detail = res.statusText;
413
+ }
414
+ throw new BrokerCallError(detail ?? `event emit failed: ${res.status}`, target, res.status);
415
+ }
416
+ }
417
+ async function brokerEmitSandboxed(target, payload, options = {}) {
418
+ await sandboxRequest({
419
+ kind: "palette.broker.emit",
420
+ target,
421
+ payload: payload ?? {},
422
+ callerAppId: options.callerAppId
423
+ });
424
+ }
425
+ function sandboxRequest(message) {
426
+ if (typeof window === "undefined") {
427
+ return Promise.reject(new BrokerCallError("sandbox bridge unavailable", message.target));
428
+ }
429
+ return new Promise((resolve, reject) => {
430
+ const id = `${Date.now()}.${Math.random().toString(36).slice(2)}`;
431
+ const onMessage = (ev) => {
432
+ const data = ev.data;
433
+ if (!data || data.id !== id) return;
434
+ window.removeEventListener("message", onMessage);
435
+ if (data.error) {
436
+ reject(new BrokerCallError(data.error.message, message.target, data.error.status));
437
+ } else {
438
+ resolve(data.result);
439
+ }
440
+ };
441
+ window.addEventListener("message", onMessage);
442
+ window.parent?.postMessage({ ...message, id }, "*");
443
+ });
444
+ }
445
+ var broker = {
446
+ async call(target, payload, options) {
447
+ return isSandboxRuntime() ? brokerCallSandboxed(target, payload, options) : brokerCallDirect(target, payload, options);
448
+ },
449
+ async emit(target, payload, options) {
450
+ if (isSandboxRuntime()) return brokerEmitSandboxed(target, payload, options);
451
+ return brokerEmitDirect(target, payload, options);
452
+ }
453
+ };
454
+ function servicesProxy(namespaceVersion) {
455
+ if (!namespaceVersion.includes("/")) {
456
+ throw new BrokerCallError("namespaceVersion must look like 'org/v1'", namespaceVersion);
457
+ }
458
+ const buildProxy = (segments) => new Proxy(function() {
459
+ }, {
460
+ get(_target, prop) {
461
+ if (typeof prop !== "string") return void 0;
462
+ return buildProxy([...segments, prop]);
463
+ },
464
+ apply(_target, _thisArg, args) {
465
+ const method = segments.join(".");
466
+ if (!method) {
467
+ throw new BrokerCallError("missing method name", namespaceVersion);
468
+ }
469
+ const [payload, options] = args;
470
+ return broker.call(`${namespaceVersion}#${method}`, payload, options);
471
+ }
472
+ });
473
+ return buildProxy([]);
474
+ }
475
+
476
+ // src/events.ts
477
+ var SUBSCRIBERS = /* @__PURE__ */ new Map();
478
+ var activeStream = null;
479
+ var activeStreamUrl = null;
480
+ var sandboxBound = false;
481
+ function currentPluginId2() {
482
+ if (typeof window === "undefined") return void 0;
483
+ const match = window.location.pathname.match(/\/apps\/([^/?#]+)/);
484
+ return match ? decodeURIComponent(match[1]) : void 0;
485
+ }
486
+ function streamUrl() {
487
+ const params = new URLSearchParams();
488
+ const targets = Array.from(SUBSCRIBERS.keys());
489
+ if (targets.length) params.set("targets", targets.join(","));
490
+ const callerAppId = currentPluginId2();
491
+ if (callerAppId) params.set("caller_app_id", callerAppId);
492
+ const query = params.toString();
493
+ return `${getBaseUrl()}/api/v1/os-broker/events/stream${query ? `?${query}` : ""}`;
494
+ }
495
+ function ensureStream() {
496
+ if (activeStream || typeof window === "undefined") return;
497
+ activeStreamUrl = streamUrl();
498
+ activeStream = new EventSource(activeStreamUrl, { withCredentials: true });
499
+ activeStream.onmessage = (ev) => {
500
+ try {
501
+ const data = JSON.parse(ev.data);
502
+ const handlers = SUBSCRIBERS.get(data.target);
503
+ if (!handlers) return;
504
+ for (const handler of handlers) {
505
+ try {
506
+ handler(data.payload, data.meta);
507
+ } catch (err) {
508
+ console.error("palette.events handler threw", err);
509
+ }
510
+ }
511
+ } catch (err) {
512
+ console.warn("palette.events: malformed event", err);
513
+ }
514
+ };
515
+ activeStream.onerror = () => {
516
+ };
517
+ }
518
+ function refreshStream() {
519
+ if (typeof window === "undefined" || isSandboxRuntime()) return;
520
+ const nextUrl = streamUrl();
521
+ if (activeStream && activeStreamUrl === nextUrl) return;
522
+ activeStream?.close();
523
+ activeStream = null;
524
+ activeStreamUrl = null;
525
+ if (SUBSCRIBERS.size > 0) ensureStream();
526
+ }
527
+ function ensureSandboxBound() {
528
+ if (sandboxBound || typeof window === "undefined") return;
529
+ sandboxBound = true;
530
+ window.addEventListener("message", (ev) => {
531
+ const data = ev.data;
532
+ if (!data || data.kind !== "palette.broker.event" || !data.target) return;
533
+ const handlers = SUBSCRIBERS.get(data.target);
534
+ if (!handlers) return;
535
+ const meta = data.meta ?? { target: data.target, organizationId: null, occurredAt: (/* @__PURE__ */ new Date()).toISOString() };
536
+ for (const handler of handlers) {
537
+ try {
538
+ handler(data.payload, meta);
539
+ } catch (err) {
540
+ console.error("palette.events handler threw", err);
541
+ }
542
+ }
543
+ });
544
+ }
545
+ var events = {
546
+ on(target, handler) {
547
+ let handlers = SUBSCRIBERS.get(target);
548
+ if (!handlers) {
549
+ handlers = /* @__PURE__ */ new Set();
550
+ SUBSCRIBERS.set(target, handlers);
551
+ }
552
+ handlers.add(handler);
553
+ if (isSandboxRuntime()) {
554
+ ensureSandboxBound();
555
+ window.parent?.postMessage({ kind: "palette.broker.subscribe", target }, "*");
556
+ } else {
557
+ refreshStream();
558
+ }
559
+ return () => {
560
+ handlers.delete(handler);
561
+ if (handlers.size === 0) {
562
+ SUBSCRIBERS.delete(target);
563
+ if (isSandboxRuntime()) {
564
+ window.parent?.postMessage({ kind: "palette.broker.unsubscribe", target }, "*");
565
+ } else {
566
+ refreshStream();
567
+ }
568
+ }
569
+ };
570
+ },
571
+ async emit(target, payload) {
572
+ await broker.emit(target, payload);
573
+ },
574
+ async publishDurable(target, payload) {
575
+ await broker.emit(target, payload);
576
+ },
577
+ async emitDurable(target, payload) {
578
+ await broker.emit(target, payload);
579
+ },
580
+ /** Diagnostic: list active subscriptions in this page. */
581
+ active() {
582
+ return Array.from(SUBSCRIBERS.keys());
583
+ }
584
+ };
585
+
320
586
  // src/permissions.ts
321
587
  function hasPermission(ctx, permission) {
322
588
  return ctx.permissions?.includes(permission) ?? false;
@@ -646,9 +912,21 @@ function createPaletteClient(ctx) {
646
912
  success: (message) => ctx?.showToast(message, "success"),
647
913
  error: (message) => ctx?.showToast(message, "error"),
648
914
  info: (message) => ctx?.showToast(message, "info")
649
- }
915
+ },
916
+ /**
917
+ * OS-broker RPC. Typed entry point: `palette.services("org/v1").members.list({...})`.
918
+ * The proxy resolves a dotted method chain into a qualified target string
919
+ * and dispatches via the broker (in-process for plugins on the host,
920
+ * postMessage for sandboxed iframe apps).
921
+ */
922
+ services: (namespaceVersion) => servicesProxy(namespaceVersion),
923
+ /** Low-level broker accessor when you don't want the typed proxy. */
924
+ broker,
925
+ /** OS-broker pub/sub. `events.on(target, fn)` returns an unsubscribe function. */
926
+ events
650
927
  };
651
928
  }
929
+ var palette = createPaletteClient();
652
930
 
653
931
  // src/i18n.ts
654
932
  import { useCallback } from "react";
@@ -1119,8 +1397,11 @@ function usePluginChat(agentId) {
1119
1397
  };
1120
1398
  }
1121
1399
  export {
1400
+ BrokerCallError,
1401
+ CrossAppGrantError,
1122
1402
  DataRoomClient,
1123
1403
  Link,
1404
+ MissingDependencyError,
1124
1405
  OrganizationClient,
1125
1406
  PaletteApiError,
1126
1407
  PaletteAppRouter,
@@ -1130,12 +1411,14 @@ export {
1130
1411
  UserClient,
1131
1412
  apiFetch,
1132
1413
  apiUpload,
1414
+ broker,
1133
1415
  createMockPlatformContext,
1134
1416
  createPaletteClient,
1135
1417
  createSandboxBridge,
1136
1418
  dataRooms,
1137
1419
  disconnectConnection,
1138
1420
  errorFromResponse,
1421
+ events,
1139
1422
  getBaseUrl,
1140
1423
  getConnections,
1141
1424
  getInstallConfig,
@@ -1146,7 +1429,9 @@ export {
1146
1429
  isSandboxRuntime,
1147
1430
  normalizePaletteLanguage,
1148
1431
  notFound,
1432
+ palette,
1149
1433
  requireConnection,
1434
+ servicesProxy,
1150
1435
  setBaseUrl,
1151
1436
  startConnection,
1152
1437
  translate,