@palettelab/sdk 0.1.19 → 0.1.21

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,6 +20,7 @@ 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,
23
24
  DataRoomClient: () => DataRoomClient,
24
25
  Link: () => Link,
25
26
  OrganizationClient: () => OrganizationClient,
@@ -31,12 +32,14 @@ __export(src_exports, {
31
32
  UserClient: () => UserClient,
32
33
  apiFetch: () => apiFetch,
33
34
  apiUpload: () => apiUpload,
35
+ broker: () => broker,
34
36
  createMockPlatformContext: () => createMockPlatformContext,
35
37
  createPaletteClient: () => createPaletteClient,
36
38
  createSandboxBridge: () => createSandboxBridge,
37
39
  dataRooms: () => dataRooms,
38
40
  disconnectConnection: () => disconnectConnection,
39
41
  errorFromResponse: () => errorFromResponse,
42
+ events: () => events,
40
43
  getBaseUrl: () => getBaseUrl,
41
44
  getConnections: () => getConnections,
42
45
  getInstallConfig: () => getInstallConfig,
@@ -47,7 +50,9 @@ __export(src_exports, {
47
50
  isSandboxRuntime: () => isSandboxRuntime,
48
51
  normalizePaletteLanguage: () => normalizePaletteLanguage,
49
52
  notFound: () => notFound,
53
+ palette: () => palette,
50
54
  requireConnection: () => requireConnection,
55
+ servicesProxy: () => servicesProxy,
51
56
  setBaseUrl: () => setBaseUrl,
52
57
  startConnection: () => startConnection,
53
58
  translate: () => translate,
@@ -385,6 +390,238 @@ var DataRoomClient = class {
385
390
  };
386
391
  var dataRooms = new DataRoomClient();
387
392
 
393
+ // src/services.ts
394
+ var BrokerCallError = class extends Error {
395
+ constructor(message, target, status) {
396
+ super(message);
397
+ this.target = target;
398
+ this.status = status;
399
+ this.name = "BrokerCallError";
400
+ }
401
+ };
402
+ var CALLER_HEADER = "X-Palette-Caller-App";
403
+ function currentPluginId() {
404
+ if (typeof window === "undefined") return void 0;
405
+ const match = window.location.pathname.match(/\/apps\/([^/?#]+)/);
406
+ return match ? decodeURIComponent(match[1]) : void 0;
407
+ }
408
+ async function brokerCallDirect(target, payload, options = {}) {
409
+ const callerAppId = options.callerAppId ?? currentPluginId();
410
+ const headers = {};
411
+ if (callerAppId) headers[CALLER_HEADER] = callerAppId;
412
+ const res = await apiFetch("/api/v1/os-broker/dispatch", {
413
+ method: "POST",
414
+ headers,
415
+ body: JSON.stringify({ target, payload: payload ?? {}, caller_app_id: callerAppId }),
416
+ signal: options.signal
417
+ });
418
+ if (!res.ok) {
419
+ let detail;
420
+ try {
421
+ detail = (await res.json()).detail;
422
+ } catch {
423
+ detail = res.statusText;
424
+ }
425
+ throw new BrokerCallError(detail ?? `broker dispatch failed: ${res.status}`, target, res.status);
426
+ }
427
+ const body = await res.json();
428
+ return body.result;
429
+ }
430
+ async function brokerCallSandboxed(target, payload, options = {}) {
431
+ return sandboxRequest({
432
+ kind: "palette.broker.call",
433
+ target,
434
+ payload: payload ?? {},
435
+ callerAppId: options.callerAppId
436
+ });
437
+ }
438
+ async function brokerEmitDirect(target, payload, options = {}) {
439
+ const callerAppId = options.callerAppId ?? currentPluginId();
440
+ if (!callerAppId) {
441
+ throw new BrokerCallError(
442
+ "events.emit requires a caller app id \u2014 only plugins may emit declared events",
443
+ target
444
+ );
445
+ }
446
+ const res = await apiFetch("/api/v1/os-broker/events/emit", {
447
+ method: "POST",
448
+ headers: { [CALLER_HEADER]: callerAppId },
449
+ body: JSON.stringify({ target, payload: payload ?? {}, caller_app_id: callerAppId }),
450
+ signal: options.signal
451
+ });
452
+ if (!res.ok) {
453
+ let detail;
454
+ try {
455
+ detail = (await res.json()).detail;
456
+ } catch {
457
+ detail = res.statusText;
458
+ }
459
+ throw new BrokerCallError(detail ?? `event emit failed: ${res.status}`, target, res.status);
460
+ }
461
+ }
462
+ async function brokerEmitSandboxed(target, payload, options = {}) {
463
+ await sandboxRequest({
464
+ kind: "palette.broker.emit",
465
+ target,
466
+ payload: payload ?? {},
467
+ callerAppId: options.callerAppId
468
+ });
469
+ }
470
+ function sandboxRequest(message) {
471
+ if (typeof window === "undefined") {
472
+ return Promise.reject(new BrokerCallError("sandbox bridge unavailable", message.target));
473
+ }
474
+ return new Promise((resolve, reject) => {
475
+ const id = `${Date.now()}.${Math.random().toString(36).slice(2)}`;
476
+ const onMessage = (ev) => {
477
+ const data = ev.data;
478
+ if (!data || data.id !== id) return;
479
+ window.removeEventListener("message", onMessage);
480
+ if (data.error) {
481
+ reject(new BrokerCallError(data.error.message, message.target, data.error.status));
482
+ } else {
483
+ resolve(data.result);
484
+ }
485
+ };
486
+ window.addEventListener("message", onMessage);
487
+ window.parent?.postMessage({ ...message, id }, "*");
488
+ });
489
+ }
490
+ var broker = {
491
+ async call(target, payload, options) {
492
+ return isSandboxRuntime() ? brokerCallSandboxed(target, payload, options) : brokerCallDirect(target, payload, options);
493
+ },
494
+ async emit(target, payload, options) {
495
+ if (isSandboxRuntime()) return brokerEmitSandboxed(target, payload, options);
496
+ return brokerEmitDirect(target, payload, options);
497
+ }
498
+ };
499
+ function servicesProxy(namespaceVersion) {
500
+ if (!namespaceVersion.includes("/")) {
501
+ throw new BrokerCallError("namespaceVersion must look like 'org/v1'", namespaceVersion);
502
+ }
503
+ const buildProxy = (segments) => new Proxy(function() {
504
+ }, {
505
+ get(_target, prop) {
506
+ if (typeof prop !== "string") return void 0;
507
+ return buildProxy([...segments, prop]);
508
+ },
509
+ apply(_target, _thisArg, args) {
510
+ const method = segments.join(".");
511
+ if (!method) {
512
+ throw new BrokerCallError("missing method name", namespaceVersion);
513
+ }
514
+ const [payload, options] = args;
515
+ return broker.call(`${namespaceVersion}#${method}`, payload, options);
516
+ }
517
+ });
518
+ return buildProxy([]);
519
+ }
520
+
521
+ // src/events.ts
522
+ var SUBSCRIBERS = /* @__PURE__ */ new Map();
523
+ var activeStream = null;
524
+ var activeStreamUrl = null;
525
+ var sandboxBound = false;
526
+ function currentPluginId2() {
527
+ if (typeof window === "undefined") return void 0;
528
+ const match = window.location.pathname.match(/\/apps\/([^/?#]+)/);
529
+ return match ? decodeURIComponent(match[1]) : void 0;
530
+ }
531
+ function streamUrl() {
532
+ const params = new URLSearchParams();
533
+ const targets = Array.from(SUBSCRIBERS.keys());
534
+ if (targets.length) params.set("targets", targets.join(","));
535
+ const callerAppId = currentPluginId2();
536
+ if (callerAppId) params.set("caller_app_id", callerAppId);
537
+ const query = params.toString();
538
+ return `/api/v1/os-broker/events/stream${query ? `?${query}` : ""}`;
539
+ }
540
+ function ensureStream() {
541
+ if (activeStream || typeof window === "undefined") return;
542
+ activeStreamUrl = streamUrl();
543
+ activeStream = new EventSource(activeStreamUrl, { withCredentials: true });
544
+ activeStream.onmessage = (ev) => {
545
+ try {
546
+ const data = JSON.parse(ev.data);
547
+ const handlers = SUBSCRIBERS.get(data.target);
548
+ if (!handlers) return;
549
+ for (const handler of handlers) {
550
+ try {
551
+ handler(data.payload, data.meta);
552
+ } catch (err) {
553
+ console.error("palette.events handler threw", err);
554
+ }
555
+ }
556
+ } catch (err) {
557
+ console.warn("palette.events: malformed event", err);
558
+ }
559
+ };
560
+ activeStream.onerror = () => {
561
+ };
562
+ }
563
+ function refreshStream() {
564
+ if (typeof window === "undefined" || isSandboxRuntime()) return;
565
+ const nextUrl = streamUrl();
566
+ if (activeStream && activeStreamUrl === nextUrl) return;
567
+ activeStream?.close();
568
+ activeStream = null;
569
+ activeStreamUrl = null;
570
+ if (SUBSCRIBERS.size > 0) ensureStream();
571
+ }
572
+ function ensureSandboxBound() {
573
+ if (sandboxBound || typeof window === "undefined") return;
574
+ sandboxBound = true;
575
+ window.addEventListener("message", (ev) => {
576
+ const data = ev.data;
577
+ if (!data || data.kind !== "palette.broker.event" || !data.target) return;
578
+ const handlers = SUBSCRIBERS.get(data.target);
579
+ if (!handlers) return;
580
+ const meta = data.meta ?? { target: data.target, organizationId: null, occurredAt: (/* @__PURE__ */ new Date()).toISOString() };
581
+ for (const handler of handlers) {
582
+ try {
583
+ handler(data.payload, meta);
584
+ } catch (err) {
585
+ console.error("palette.events handler threw", err);
586
+ }
587
+ }
588
+ });
589
+ }
590
+ var events = {
591
+ on(target, handler) {
592
+ let handlers = SUBSCRIBERS.get(target);
593
+ if (!handlers) {
594
+ handlers = /* @__PURE__ */ new Set();
595
+ SUBSCRIBERS.set(target, handlers);
596
+ }
597
+ handlers.add(handler);
598
+ if (isSandboxRuntime()) {
599
+ ensureSandboxBound();
600
+ window.parent?.postMessage({ kind: "palette.broker.subscribe", target }, "*");
601
+ } else {
602
+ refreshStream();
603
+ }
604
+ return () => {
605
+ handlers.delete(handler);
606
+ if (handlers.size === 0) {
607
+ SUBSCRIBERS.delete(target);
608
+ if (isSandboxRuntime()) {
609
+ window.parent?.postMessage({ kind: "palette.broker.unsubscribe", target }, "*");
610
+ } else {
611
+ refreshStream();
612
+ }
613
+ }
614
+ };
615
+ },
616
+ async emit(target, payload) {
617
+ await broker.emit(target, payload);
618
+ },
619
+ /** Diagnostic: list active subscriptions in this page. */
620
+ active() {
621
+ return Array.from(SUBSCRIBERS.keys());
622
+ }
623
+ };
624
+
388
625
  // src/permissions.ts
389
626
  function hasPermission(ctx, permission) {
390
627
  return ctx.permissions?.includes(permission) ?? false;
@@ -714,9 +951,21 @@ function createPaletteClient(ctx) {
714
951
  success: (message) => ctx?.showToast(message, "success"),
715
952
  error: (message) => ctx?.showToast(message, "error"),
716
953
  info: (message) => ctx?.showToast(message, "info")
717
- }
954
+ },
955
+ /**
956
+ * OS-broker RPC. Typed entry point: `palette.services("org/v1").members.list({...})`.
957
+ * The proxy resolves a dotted method chain into a qualified target string
958
+ * and dispatches via the broker (in-process for plugins on the host,
959
+ * postMessage for sandboxed iframe apps).
960
+ */
961
+ services: (namespaceVersion) => servicesProxy(namespaceVersion),
962
+ /** Low-level broker accessor when you don't want the typed proxy. */
963
+ broker,
964
+ /** OS-broker pub/sub. `events.on(target, fn)` returns an unsubscribe function. */
965
+ events
718
966
  };
719
967
  }
968
+ var palette = createPaletteClient();
720
969
 
721
970
  // src/i18n.ts
722
971
  var import_react3 = require("react");
@@ -841,6 +1090,21 @@ function currentSearchParams() {
841
1090
  if (typeof window === "undefined") return new URLSearchParams();
842
1091
  return new URLSearchParams(window.location.search);
843
1092
  }
1093
+ function preserveCurrentPreviewParams(path, pluginId) {
1094
+ if (typeof window === "undefined" || !pluginId || /^https?:\/\//i.test(path)) return path;
1095
+ const currentParams = new URLSearchParams(window.location.search);
1096
+ const previewPublishId = currentParams.get("preview_publish_id");
1097
+ const previewToken = currentParams.get("preview_token");
1098
+ if (!previewPublishId || !previewToken) return path;
1099
+ const [beforeHash, hash = ""] = path.split("#", 2);
1100
+ const [pathname, query = ""] = beforeHash.split("?", 2);
1101
+ if (pathname !== `/apps/${pluginId}` && !pathname.startsWith(`/apps/${pluginId}/`)) return path;
1102
+ const params = new URLSearchParams(query);
1103
+ params.set("preview_publish_id", previewPublishId);
1104
+ params.set("preview_token", previewToken);
1105
+ const serialized = params.toString();
1106
+ return `${pathname}${serialized ? `?${serialized}` : ""}${hash ? `#${hash}` : ""}`;
1107
+ }
844
1108
  var PaletteRouteErrorBoundary = class extends import_react4.Component {
845
1109
  constructor() {
846
1110
  super(...arguments);
@@ -918,8 +1182,9 @@ function PaletteAppRouter({
918
1182
  const currentPath = typeof window === "undefined" ? "" : window.location.pathname;
919
1183
  const inPalettePath = platform.pluginId && (currentPath.startsWith(`/apps/${platform.pluginId}`) || platform.routePath !== void 0);
920
1184
  const osPath = inPalettePath && platform.pluginId ? `/apps/${platform.pluginId}${pathname === "/" ? "" : pathname}${queryPart ? `?${queryPart}` : ""}` : next;
921
- if (replace && typeof window !== "undefined") window.history.replaceState(null, "", osPath);
922
- else platform.navigate(osPath);
1185
+ const preservedOsPath = preserveCurrentPreviewParams(osPath, platform.pluginId);
1186
+ if (replace && typeof window !== "undefined") window.history.replaceState(null, "", preservedOsPath);
1187
+ else platform.navigate(preservedOsPath);
923
1188
  }, [platform]);
924
1189
  const state = (0, import_react4.useMemo)(() => ({
925
1190
  pathname: location.pathname,
@@ -1163,6 +1428,7 @@ function usePluginChat(agentId) {
1163
1428
  }
1164
1429
  // Annotate the CommonJS export names for ESM import in node:
1165
1430
  0 && (module.exports = {
1431
+ BrokerCallError,
1166
1432
  DataRoomClient,
1167
1433
  Link,
1168
1434
  OrganizationClient,
@@ -1174,12 +1440,14 @@ function usePluginChat(agentId) {
1174
1440
  UserClient,
1175
1441
  apiFetch,
1176
1442
  apiUpload,
1443
+ broker,
1177
1444
  createMockPlatformContext,
1178
1445
  createPaletteClient,
1179
1446
  createSandboxBridge,
1180
1447
  dataRooms,
1181
1448
  disconnectConnection,
1182
1449
  errorFromResponse,
1450
+ events,
1183
1451
  getBaseUrl,
1184
1452
  getConnections,
1185
1453
  getInstallConfig,
@@ -1190,7 +1458,9 @@ function usePluginChat(agentId) {
1190
1458
  isSandboxRuntime,
1191
1459
  normalizePaletteLanguage,
1192
1460
  notFound,
1461
+ palette,
1193
1462
  requireConnection,
1463
+ servicesProxy,
1194
1464
  setBaseUrl,
1195
1465
  startConnection,
1196
1466
  translate,
package/dist/index.mjs CHANGED
@@ -317,6 +317,238 @@ 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 CALLER_HEADER = "X-Palette-Caller-App";
330
+ function currentPluginId() {
331
+ if (typeof window === "undefined") return void 0;
332
+ const match = window.location.pathname.match(/\/apps\/([^/?#]+)/);
333
+ return match ? decodeURIComponent(match[1]) : void 0;
334
+ }
335
+ async function brokerCallDirect(target, payload, options = {}) {
336
+ const callerAppId = options.callerAppId ?? currentPluginId();
337
+ const headers = {};
338
+ if (callerAppId) headers[CALLER_HEADER] = callerAppId;
339
+ const res = await apiFetch("/api/v1/os-broker/dispatch", {
340
+ method: "POST",
341
+ headers,
342
+ body: JSON.stringify({ target, payload: payload ?? {}, caller_app_id: callerAppId }),
343
+ signal: options.signal
344
+ });
345
+ if (!res.ok) {
346
+ let detail;
347
+ try {
348
+ detail = (await res.json()).detail;
349
+ } catch {
350
+ detail = res.statusText;
351
+ }
352
+ throw new BrokerCallError(detail ?? `broker dispatch failed: ${res.status}`, target, res.status);
353
+ }
354
+ const body = await res.json();
355
+ return body.result;
356
+ }
357
+ async function brokerCallSandboxed(target, payload, options = {}) {
358
+ return sandboxRequest({
359
+ kind: "palette.broker.call",
360
+ target,
361
+ payload: payload ?? {},
362
+ callerAppId: options.callerAppId
363
+ });
364
+ }
365
+ async function brokerEmitDirect(target, payload, options = {}) {
366
+ const callerAppId = options.callerAppId ?? currentPluginId();
367
+ if (!callerAppId) {
368
+ throw new BrokerCallError(
369
+ "events.emit requires a caller app id \u2014 only plugins may emit declared events",
370
+ target
371
+ );
372
+ }
373
+ const res = await apiFetch("/api/v1/os-broker/events/emit", {
374
+ method: "POST",
375
+ headers: { [CALLER_HEADER]: callerAppId },
376
+ body: JSON.stringify({ target, payload: payload ?? {}, caller_app_id: callerAppId }),
377
+ signal: options.signal
378
+ });
379
+ if (!res.ok) {
380
+ let detail;
381
+ try {
382
+ detail = (await res.json()).detail;
383
+ } catch {
384
+ detail = res.statusText;
385
+ }
386
+ throw new BrokerCallError(detail ?? `event emit failed: ${res.status}`, target, res.status);
387
+ }
388
+ }
389
+ async function brokerEmitSandboxed(target, payload, options = {}) {
390
+ await sandboxRequest({
391
+ kind: "palette.broker.emit",
392
+ target,
393
+ payload: payload ?? {},
394
+ callerAppId: options.callerAppId
395
+ });
396
+ }
397
+ function sandboxRequest(message) {
398
+ if (typeof window === "undefined") {
399
+ return Promise.reject(new BrokerCallError("sandbox bridge unavailable", message.target));
400
+ }
401
+ return new Promise((resolve, reject) => {
402
+ const id = `${Date.now()}.${Math.random().toString(36).slice(2)}`;
403
+ const onMessage = (ev) => {
404
+ const data = ev.data;
405
+ if (!data || data.id !== id) return;
406
+ window.removeEventListener("message", onMessage);
407
+ if (data.error) {
408
+ reject(new BrokerCallError(data.error.message, message.target, data.error.status));
409
+ } else {
410
+ resolve(data.result);
411
+ }
412
+ };
413
+ window.addEventListener("message", onMessage);
414
+ window.parent?.postMessage({ ...message, id }, "*");
415
+ });
416
+ }
417
+ var broker = {
418
+ async call(target, payload, options) {
419
+ return isSandboxRuntime() ? brokerCallSandboxed(target, payload, options) : brokerCallDirect(target, payload, options);
420
+ },
421
+ async emit(target, payload, options) {
422
+ if (isSandboxRuntime()) return brokerEmitSandboxed(target, payload, options);
423
+ return brokerEmitDirect(target, payload, options);
424
+ }
425
+ };
426
+ function servicesProxy(namespaceVersion) {
427
+ if (!namespaceVersion.includes("/")) {
428
+ throw new BrokerCallError("namespaceVersion must look like 'org/v1'", namespaceVersion);
429
+ }
430
+ const buildProxy = (segments) => new Proxy(function() {
431
+ }, {
432
+ get(_target, prop) {
433
+ if (typeof prop !== "string") return void 0;
434
+ return buildProxy([...segments, prop]);
435
+ },
436
+ apply(_target, _thisArg, args) {
437
+ const method = segments.join(".");
438
+ if (!method) {
439
+ throw new BrokerCallError("missing method name", namespaceVersion);
440
+ }
441
+ const [payload, options] = args;
442
+ return broker.call(`${namespaceVersion}#${method}`, payload, options);
443
+ }
444
+ });
445
+ return buildProxy([]);
446
+ }
447
+
448
+ // src/events.ts
449
+ var SUBSCRIBERS = /* @__PURE__ */ new Map();
450
+ var activeStream = null;
451
+ var activeStreamUrl = null;
452
+ var sandboxBound = false;
453
+ function currentPluginId2() {
454
+ if (typeof window === "undefined") return void 0;
455
+ const match = window.location.pathname.match(/\/apps\/([^/?#]+)/);
456
+ return match ? decodeURIComponent(match[1]) : void 0;
457
+ }
458
+ function streamUrl() {
459
+ const params = new URLSearchParams();
460
+ const targets = Array.from(SUBSCRIBERS.keys());
461
+ if (targets.length) params.set("targets", targets.join(","));
462
+ const callerAppId = currentPluginId2();
463
+ if (callerAppId) params.set("caller_app_id", callerAppId);
464
+ const query = params.toString();
465
+ return `/api/v1/os-broker/events/stream${query ? `?${query}` : ""}`;
466
+ }
467
+ function ensureStream() {
468
+ if (activeStream || typeof window === "undefined") return;
469
+ activeStreamUrl = streamUrl();
470
+ activeStream = new EventSource(activeStreamUrl, { withCredentials: true });
471
+ activeStream.onmessage = (ev) => {
472
+ try {
473
+ const data = JSON.parse(ev.data);
474
+ const handlers = SUBSCRIBERS.get(data.target);
475
+ if (!handlers) return;
476
+ for (const handler of handlers) {
477
+ try {
478
+ handler(data.payload, data.meta);
479
+ } catch (err) {
480
+ console.error("palette.events handler threw", err);
481
+ }
482
+ }
483
+ } catch (err) {
484
+ console.warn("palette.events: malformed event", err);
485
+ }
486
+ };
487
+ activeStream.onerror = () => {
488
+ };
489
+ }
490
+ function refreshStream() {
491
+ if (typeof window === "undefined" || isSandboxRuntime()) return;
492
+ const nextUrl = streamUrl();
493
+ if (activeStream && activeStreamUrl === nextUrl) return;
494
+ activeStream?.close();
495
+ activeStream = null;
496
+ activeStreamUrl = null;
497
+ if (SUBSCRIBERS.size > 0) ensureStream();
498
+ }
499
+ function ensureSandboxBound() {
500
+ if (sandboxBound || typeof window === "undefined") return;
501
+ sandboxBound = true;
502
+ window.addEventListener("message", (ev) => {
503
+ const data = ev.data;
504
+ if (!data || data.kind !== "palette.broker.event" || !data.target) return;
505
+ const handlers = SUBSCRIBERS.get(data.target);
506
+ if (!handlers) return;
507
+ const meta = data.meta ?? { target: data.target, organizationId: null, occurredAt: (/* @__PURE__ */ new Date()).toISOString() };
508
+ for (const handler of handlers) {
509
+ try {
510
+ handler(data.payload, meta);
511
+ } catch (err) {
512
+ console.error("palette.events handler threw", err);
513
+ }
514
+ }
515
+ });
516
+ }
517
+ var events = {
518
+ on(target, handler) {
519
+ let handlers = SUBSCRIBERS.get(target);
520
+ if (!handlers) {
521
+ handlers = /* @__PURE__ */ new Set();
522
+ SUBSCRIBERS.set(target, handlers);
523
+ }
524
+ handlers.add(handler);
525
+ if (isSandboxRuntime()) {
526
+ ensureSandboxBound();
527
+ window.parent?.postMessage({ kind: "palette.broker.subscribe", target }, "*");
528
+ } else {
529
+ refreshStream();
530
+ }
531
+ return () => {
532
+ handlers.delete(handler);
533
+ if (handlers.size === 0) {
534
+ SUBSCRIBERS.delete(target);
535
+ if (isSandboxRuntime()) {
536
+ window.parent?.postMessage({ kind: "palette.broker.unsubscribe", target }, "*");
537
+ } else {
538
+ refreshStream();
539
+ }
540
+ }
541
+ };
542
+ },
543
+ async emit(target, payload) {
544
+ await broker.emit(target, payload);
545
+ },
546
+ /** Diagnostic: list active subscriptions in this page. */
547
+ active() {
548
+ return Array.from(SUBSCRIBERS.keys());
549
+ }
550
+ };
551
+
320
552
  // src/permissions.ts
321
553
  function hasPermission(ctx, permission) {
322
554
  return ctx.permissions?.includes(permission) ?? false;
@@ -646,9 +878,21 @@ function createPaletteClient(ctx) {
646
878
  success: (message) => ctx?.showToast(message, "success"),
647
879
  error: (message) => ctx?.showToast(message, "error"),
648
880
  info: (message) => ctx?.showToast(message, "info")
649
- }
881
+ },
882
+ /**
883
+ * OS-broker RPC. Typed entry point: `palette.services("org/v1").members.list({...})`.
884
+ * The proxy resolves a dotted method chain into a qualified target string
885
+ * and dispatches via the broker (in-process for plugins on the host,
886
+ * postMessage for sandboxed iframe apps).
887
+ */
888
+ services: (namespaceVersion) => servicesProxy(namespaceVersion),
889
+ /** Low-level broker accessor when you don't want the typed proxy. */
890
+ broker,
891
+ /** OS-broker pub/sub. `events.on(target, fn)` returns an unsubscribe function. */
892
+ events
650
893
  };
651
894
  }
895
+ var palette = createPaletteClient();
652
896
 
653
897
  // src/i18n.ts
654
898
  import { useCallback } from "react";
@@ -782,6 +1026,21 @@ function currentSearchParams() {
782
1026
  if (typeof window === "undefined") return new URLSearchParams();
783
1027
  return new URLSearchParams(window.location.search);
784
1028
  }
1029
+ function preserveCurrentPreviewParams(path, pluginId) {
1030
+ if (typeof window === "undefined" || !pluginId || /^https?:\/\//i.test(path)) return path;
1031
+ const currentParams = new URLSearchParams(window.location.search);
1032
+ const previewPublishId = currentParams.get("preview_publish_id");
1033
+ const previewToken = currentParams.get("preview_token");
1034
+ if (!previewPublishId || !previewToken) return path;
1035
+ const [beforeHash, hash = ""] = path.split("#", 2);
1036
+ const [pathname, query = ""] = beforeHash.split("?", 2);
1037
+ if (pathname !== `/apps/${pluginId}` && !pathname.startsWith(`/apps/${pluginId}/`)) return path;
1038
+ const params = new URLSearchParams(query);
1039
+ params.set("preview_publish_id", previewPublishId);
1040
+ params.set("preview_token", previewToken);
1041
+ const serialized = params.toString();
1042
+ return `${pathname}${serialized ? `?${serialized}` : ""}${hash ? `#${hash}` : ""}`;
1043
+ }
785
1044
  var PaletteRouteErrorBoundary = class extends Component {
786
1045
  constructor() {
787
1046
  super(...arguments);
@@ -859,8 +1118,9 @@ function PaletteAppRouter({
859
1118
  const currentPath = typeof window === "undefined" ? "" : window.location.pathname;
860
1119
  const inPalettePath = platform.pluginId && (currentPath.startsWith(`/apps/${platform.pluginId}`) || platform.routePath !== void 0);
861
1120
  const osPath = inPalettePath && platform.pluginId ? `/apps/${platform.pluginId}${pathname === "/" ? "" : pathname}${queryPart ? `?${queryPart}` : ""}` : next;
862
- if (replace && typeof window !== "undefined") window.history.replaceState(null, "", osPath);
863
- else platform.navigate(osPath);
1121
+ const preservedOsPath = preserveCurrentPreviewParams(osPath, platform.pluginId);
1122
+ if (replace && typeof window !== "undefined") window.history.replaceState(null, "", preservedOsPath);
1123
+ else platform.navigate(preservedOsPath);
864
1124
  }, [platform]);
865
1125
  const state = useMemo(() => ({
866
1126
  pathname: location.pathname,
@@ -1103,6 +1363,7 @@ function usePluginChat(agentId) {
1103
1363
  };
1104
1364
  }
1105
1365
  export {
1366
+ BrokerCallError,
1106
1367
  DataRoomClient,
1107
1368
  Link,
1108
1369
  OrganizationClient,
@@ -1114,12 +1375,14 @@ export {
1114
1375
  UserClient,
1115
1376
  apiFetch,
1116
1377
  apiUpload,
1378
+ broker,
1117
1379
  createMockPlatformContext,
1118
1380
  createPaletteClient,
1119
1381
  createSandboxBridge,
1120
1382
  dataRooms,
1121
1383
  disconnectConnection,
1122
1384
  errorFromResponse,
1385
+ events,
1123
1386
  getBaseUrl,
1124
1387
  getConnections,
1125
1388
  getInstallConfig,
@@ -1130,7 +1393,9 @@ export {
1130
1393
  isSandboxRuntime,
1131
1394
  normalizePaletteLanguage,
1132
1395
  notFound,
1396
+ palette,
1133
1397
  requireConnection,
1398
+ servicesProxy,
1134
1399
  setBaseUrl,
1135
1400
  startConnection,
1136
1401
  translate,