@ethisyscore/plugin-ui 1.123.0 → 1.125.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/README.md CHANGED
@@ -16,7 +16,7 @@ and the shared MUI component surface externalised to the host's instances.
16
16
  | Subpath | Key exports | Purpose |
17
17
  |---|---|---|
18
18
  | `.` | `createPortBridgeClient`, `BridgeClientContext` / `useBridgeClient`, `useBridgeTheme` / `useBridgeLocale`, `ExtensionRuntimeProvider`, `useMcpResource` / `useMcpTool` / `useMcpQuery` / `unwrapItems`, `useHostIdentity` / `useCurrentUser`, `definePlatformReactPage` / `emitNavigation` / `PlatformReactPageProps` | Bridge client + theme/locale context, brokered-MCP data hooks, host identity, and the base PlatformReact page contract |
19
- | `./platform-react` | `definePlatformReactPluginPage` / `createPluginPageDefiner`, `definePlatformReactPluginOverlay` / `useOverlayHost`, `useView` / `createUseView` / `ViewComponent` / `isComponent`, `BaseMcpService` / `useToolInvoker` / `useToolInvokerMap`, `useAuthenticatedQuery` / `useAuthenticatedQueries`, `injectPluginStyles` / `PluginStyleScope`, `createReactRouterShim`, `usePluginRealtime`, `useMcpUpload`, `useManagedLifecycle` | The plugin authoring layer: page/overlay wrappers (react-query + manifest + style root), the static `useView` adapter pattern, the MCP service base + tool invokers, pre-authenticated react-query wrappers, scoped-style injection, router shim, realtime, and file upload |
19
+ | `./platform-react` | `definePlatformReactPluginPage` / `createPluginPageDefiner`, `definePlatformReactPluginOverlay` / `useOverlayHost`, `useView` / `createUseView` / `ViewComponent` / `isComponent`, `BaseMcpService` / `useToolInvoker` / `useToolInvokerMap`, `useAuthenticatedQuery` / `useAuthenticatedQueries`, `injectPluginStyles` / `PluginStyleScope`, `createReactRouterShim`, `usePluginRealtime`, `useBackgroundOperation` / `useOperationsByParent` / `useLatestTerminalOperationByParent` / `hasKind`, `useMcpUpload`, `useManagedLifecycle` | The plugin authoring layer: page/overlay wrappers (react-query + manifest + style root), the static `useView` adapter pattern, the MCP service base + tool invokers, pre-authenticated react-query wrappers, scoped-style injection, router shim, realtime, following the plugin's own background operations, and file upload |
20
20
  | `./icons` | `NextureIcons`, `IconMap`, 470+ `Ni*` icon components, `sizeHelper` / `strokeSizeHelper`, `IconName` / `IconSize` / `IconVariant` | The gogo-ui "nexture" SVG icon set with props types and size/stroke helpers |
21
21
  | `./components/ui` | `Button`, `Card`, `Dialog`, `Table`, `TextField`, `Select`, `Tabs`, `Typography`, `Grid`, `Stack`, `Menu`, `DebouncedSearchInput`, … | MUI component re-exports plus the Dialog scroll-lock wrapper and the debounced search field. These are **host externals** — never bundled; production builds bind them to the host's MUI instance |
22
22
  | `./components/layout` | `PageHeader` / `usePageHeader` / `withPageHeader` / `buildBreadcrumbs`, `FillGrid` / `FillGridItem` / `FillGap` / `FillSpan`, sidebar-action context (`SidebarActionProvider`, `useSidebarAction` / `useSidebarActions` / `useTriggerSidebarAction`), `RouteMeta` / `RouteMetaEntry` | Layout primitives (flex-wrap grid), the page-header layer + breadcrumb builder, and the sidebar-action context/provider |
@@ -766,6 +766,147 @@ function usePluginRealtime(typeCode, handler) {
766
766
  return source.subscribe(typeCode, (payload) => ref.current(payload));
767
767
  }, [source, typeCode]);
768
768
  }
769
+
770
+ // src/platform-react/operations/asStateChanged.ts
771
+ function asStateChanged(payload) {
772
+ if (payload == null || typeof payload !== "object") {
773
+ return null;
774
+ }
775
+ const candidate = payload;
776
+ return typeof candidate.operationId === "string" && typeof candidate.eventSequence === "number" ? candidate : null;
777
+ }
778
+
779
+ // src/platform-react/operations/mergeOperation.ts
780
+ function mergeOperation(current, event) {
781
+ if (current && current.eventSequence >= event.eventSequence) {
782
+ return current;
783
+ }
784
+ const { extensionId: _extensionId, previousStatus: _previousStatus, ...fields } = event;
785
+ return { ...fields, resultPayloadJson: current?.resultPayloadJson ?? null };
786
+ }
787
+ function mergeIntoList(current, event) {
788
+ const rows = current ?? [];
789
+ const index = rows.findIndex((r) => r.operationId === event.operationId);
790
+ if (index < 0) {
791
+ return [mergeOperation(null, event), ...rows];
792
+ }
793
+ const merged = mergeOperation(rows[index], event);
794
+ if (merged === rows[index]) {
795
+ return rows;
796
+ }
797
+ const next = rows.slice();
798
+ next[index] = merged;
799
+ return next;
800
+ }
801
+
802
+ // src/platform-react/operations/queryKeys.ts
803
+ var pluginOperationKeys = {
804
+ all: ["plugin-operations"],
805
+ byId: (operationId) => ["plugin-operations", "get", operationId],
806
+ byParent: (parentEntityType, parentEntityId) => ["plugin-operations", "by-parent", parentEntityType, parentEntityId],
807
+ latestTerminalByParent: (parentEntityType, parentEntityId) => ["plugin-operations", "latest-terminal", parentEntityType, parentEntityId]
808
+ };
809
+
810
+ // src/platform-react/operations/status.ts
811
+ var TERMINAL = /* @__PURE__ */ new Set(["Succeeded", "Failed", "Cancelled"]);
812
+ function isTerminalOperationStatus(status) {
813
+ return status != null && TERMINAL.has(status);
814
+ }
815
+
816
+ // src/platform-react/operations/types.ts
817
+ var PLUGIN_OPERATION_STATE_CHANGED = "operations.plugin-operation.state-changed";
818
+ var PLUGIN_OPERATION_TOOLS = {
819
+ get: "plugin-operations:get",
820
+ listByParent: "plugin-operations:list-by-parent",
821
+ listActive: "plugin-operations:list-active",
822
+ latestTerminalByParent: "plugin-operations:latest-terminal-by-parent"
823
+ };
824
+ var OPERATION_FALLBACK_POLL_MS = 8e3;
825
+
826
+ // src/platform-react/operations/useBackgroundOperation.ts
827
+ function useBackgroundOperation(operationId) {
828
+ const queryClient = reactQuery.useQueryClient();
829
+ const realtime = extensionRuntime.usePluginRealtimeSource();
830
+ const { invoke } = extensionRuntime.useMcpTool(PLUGIN_OPERATION_TOOLS.get);
831
+ const query = reactQuery.useQuery({
832
+ queryKey: pluginOperationKeys.byId(operationId ?? ""),
833
+ enabled: Boolean(operationId),
834
+ // Never undefined: TanStack Query rejects an undefined result.
835
+ queryFn: async () => await invoke({ operationId }) ?? null,
836
+ refetchInterval: (q) => realtime === null && q.state.data != null && !isTerminalOperationStatus(q.state.data.status) ? OPERATION_FALLBACK_POLL_MS : false
837
+ });
838
+ usePluginRealtime(PLUGIN_OPERATION_STATE_CHANGED, (payload) => {
839
+ const event = asStateChanged(payload);
840
+ if (!operationId || event?.operationId !== operationId) {
841
+ return;
842
+ }
843
+ const key = pluginOperationKeys.byId(operationId);
844
+ const before = queryClient.getQueryData(key);
845
+ const merged = mergeOperation(before, event);
846
+ queryClient.setQueryData(key, merged);
847
+ if (merged !== before && isTerminalOperationStatus(merged.status)) {
848
+ void queryClient.invalidateQueries({ queryKey: key });
849
+ }
850
+ });
851
+ return query;
852
+ }
853
+
854
+ // src/platform-react/operations/hasKind.ts
855
+ var KIND_SCOPE = /^p:[0-9a-f]{32}:/i;
856
+ var PARENT_SCOPE = /^po:[0-9A-F]{32}:/i;
857
+ function hasKind(operation, unscopedKind) {
858
+ const kind = typeof operation === "string" ? operation : operation?.kind;
859
+ return kind != null && kind.replace(KIND_SCOPE, "") === unscopedKind;
860
+ }
861
+ function hasParentEntityType(operation, unscopedParentEntityType) {
862
+ const parentType = typeof operation === "string" ? operation : operation?.parentEntityType;
863
+ return parentType != null && parentType.replace(PARENT_SCOPE, "") === unscopedParentEntityType;
864
+ }
865
+
866
+ // src/platform-react/operations/useOperationsByParent.ts
867
+ function useOperationsByParent(parentEntityType, parentEntityId) {
868
+ const queryClient = reactQuery.useQueryClient();
869
+ const realtime = extensionRuntime.usePluginRealtimeSource();
870
+ const { invoke } = extensionRuntime.useMcpTool(
871
+ PLUGIN_OPERATION_TOOLS.listByParent
872
+ );
873
+ const query = reactQuery.useQuery({
874
+ queryKey: pluginOperationKeys.byParent(parentEntityType, parentEntityId ?? ""),
875
+ enabled: Boolean(parentEntityId),
876
+ queryFn: async () => (await invoke({ parentEntityType, parentEntityId }))?.items ?? [],
877
+ refetchInterval: (q) => realtime === null && (q.state.data ?? []).some((op) => !isTerminalOperationStatus(op.status)) ? OPERATION_FALLBACK_POLL_MS : false
878
+ });
879
+ usePluginRealtime(PLUGIN_OPERATION_STATE_CHANGED, (payload) => {
880
+ const event = asStateChanged(payload);
881
+ if (!parentEntityId || event == null || event.parentEntityId !== parentEntityId || !hasParentEntityType(event, parentEntityType)) {
882
+ return;
883
+ }
884
+ queryClient.setQueryData(
885
+ pluginOperationKeys.byParent(parentEntityType, parentEntityId),
886
+ (current) => mergeIntoList(current, event)
887
+ );
888
+ });
889
+ return query;
890
+ }
891
+ function useLatestTerminalOperationByParent(parentEntityType, parentEntityId) {
892
+ const queryClient = reactQuery.useQueryClient();
893
+ const { invoke } = extensionRuntime.useMcpTool(
894
+ PLUGIN_OPERATION_TOOLS.latestTerminalByParent
895
+ );
896
+ const key = pluginOperationKeys.latestTerminalByParent(parentEntityType, parentEntityId ?? "");
897
+ const query = reactQuery.useQuery({
898
+ queryKey: key,
899
+ enabled: Boolean(parentEntityId),
900
+ queryFn: async () => (await invoke({ parentEntityType, parentEntityId }))?.item ?? null
901
+ });
902
+ usePluginRealtime(PLUGIN_OPERATION_STATE_CHANGED, (payload) => {
903
+ const event = asStateChanged(payload);
904
+ if (parentEntityId && event != null && event.parentEntityId === parentEntityId && hasParentEntityType(event, parentEntityType) && isTerminalOperationStatus(event.status)) {
905
+ void queryClient.invalidateQueries({ queryKey: key });
906
+ }
907
+ });
908
+ return query;
909
+ }
769
910
  var DEFAULT_MAX_UPLOAD_BYTES = 30 * 1024 * 1024;
770
911
  function useMcpUpload(opts) {
771
912
  const transport = extensionRuntime.useExtensionRuntimeTransport(opts?.transport);
@@ -1282,9 +1423,12 @@ exports.HOST_CHROME_CONTRACT_VERSION = HOST_CHROME_CONTRACT_VERSION;
1282
1423
  exports.HostSidebarActionsContext = HostSidebarActionsContext;
1283
1424
  exports.LIST_CURRENCIES_TOOL = LIST_CURRENCIES_TOOL;
1284
1425
  exports.MissingViewFallback = MissingViewFallback;
1426
+ exports.OPERATION_FALLBACK_POLL_MS = OPERATION_FALLBACK_POLL_MS;
1285
1427
  exports.ORGANISATION_SETTINGS_URI = ORGANISATION_SETTINGS_URI;
1286
1428
  exports.OVERLAY_HOST_CONTRACT_VERSION = OVERLAY_HOST_CONTRACT_VERSION;
1287
1429
  exports.OverlayHostContext = OverlayHostContext;
1430
+ exports.PLUGIN_OPERATION_STATE_CHANGED = PLUGIN_OPERATION_STATE_CHANGED;
1431
+ exports.PLUGIN_OPERATION_TOOLS = PLUGIN_OPERATION_TOOLS;
1288
1432
  exports.PluginPortalScope = PluginPortalScope;
1289
1433
  exports.PluginStyleScope = PluginStyleScope;
1290
1434
  exports.REPORTING_CURRENCY_TOOL = REPORTING_CURRENCY_TOOL2;
@@ -1301,13 +1445,17 @@ exports.createUseView = createUseView;
1301
1445
  exports.definePlatformReactPluginOverlay = definePlatformReactPluginOverlay;
1302
1446
  exports.definePlatformReactPluginPage = definePlatformReactPluginPage;
1303
1447
  exports.deriveHostEntityToken = deriveHostEntityToken;
1448
+ exports.hasKind = hasKind;
1449
+ exports.hasParentEntityType = hasParentEntityType;
1304
1450
  exports.injectPluginStyles = injectPluginStyles;
1305
1451
  exports.isComponent = isComponent;
1306
1452
  exports.isHostChromeCompatible = isHostChromeCompatible;
1453
+ exports.isTerminalOperationStatus = isTerminalOperationStatus;
1307
1454
  exports.mcpTool = mcpTool;
1308
1455
  exports.menuConfigToHostSidebarInput = menuConfigToHostSidebarInput;
1309
1456
  exports.normaliseSlug = normaliseSlug;
1310
1457
  exports.notViaMcp = notViaMcp;
1458
+ exports.pluginOperationKeys = pluginOperationKeys;
1311
1459
  exports.reportHostChromeDiagnostic = reportHostChromeDiagnostic;
1312
1460
  exports.resolveActiveNavHref = resolveActiveNavHref;
1313
1461
  exports.resolveSurfaceBase = resolveSurfaceBase;
@@ -1316,10 +1464,13 @@ exports.surfacePathFor = surfacePathFor;
1316
1464
  exports.toHostSidebarInput = toHostSidebarInput;
1317
1465
  exports.useAuthenticatedQueries = useAuthenticatedQueries;
1318
1466
  exports.useAuthenticatedQuery = useAuthenticatedQuery;
1467
+ exports.useBackgroundOperation = useBackgroundOperation;
1319
1468
  exports.useCurrency = useCurrency;
1320
1469
  exports.useHostSidebarActions = useHostSidebarActions;
1470
+ exports.useLatestTerminalOperationByParent = useLatestTerminalOperationByParent;
1321
1471
  exports.useManagedLifecycle = useManagedLifecycle;
1322
1472
  exports.useMcpUpload = useMcpUpload;
1473
+ exports.useOperationsByParent = useOperationsByParent;
1323
1474
  exports.useOrgFormatSync = useOrgFormatSync;
1324
1475
  exports.useOverlayHost = useOverlayHost;
1325
1476
  exports.usePluginRealtime = usePluginRealtime;