@ethisyscore/extension-runtime 1.26.0 → 1.27.1
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/mock-host/index.cjs +8 -2
- package/dist/mock-host/index.cjs.map +1 -1
- package/dist/mock-host/index.js +8 -2
- package/dist/mock-host/index.js.map +1 -1
- package/dist/plugin/index.cjs +41 -2
- package/dist/plugin/index.cjs.map +1 -1
- package/dist/plugin/index.d.cts +180 -2
- package/dist/plugin/index.d.ts +180 -2
- package/dist/plugin/index.js +37 -4
- package/dist/plugin/index.js.map +1 -1
- package/package.json +1 -1
package/dist/plugin/index.d.cts
CHANGED
|
@@ -6,8 +6,186 @@ import { SduiNode, RenderMode } from '@ethisyscore/protocol';
|
|
|
6
6
|
import { RemoteConnection } from '@remote-dom/core';
|
|
7
7
|
import '../bridge-envelopes-BRKGSiSC.cjs';
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* A single client-push event delivered from the host to a plugin surface. The host
|
|
11
|
+
* relays the plugin backend's `IClientPushPublisher` events over its realtime
|
|
12
|
+
* channel (SignalR); the transport envelope's extension identity is bound by the
|
|
13
|
+
* host at mount time, so the plugin sees only the event body.
|
|
14
|
+
*
|
|
15
|
+
* SCOPE + ORDERING: which channel/user/group an event concerns is carried INSIDE
|
|
16
|
+
* `payloadJson` by the emitting plugin — the transport envelope intentionally has no
|
|
17
|
+
* group field. Consumers therefore demultiplex + order by their own payload fields
|
|
18
|
+
* (e.g. a per-channel sequence in the payload), NOT by {@link eventSequence}, which
|
|
19
|
+
* is per-group at the host and would produce false gaps when multiple groups
|
|
20
|
+
* multiplex over one connection.
|
|
21
|
+
*/
|
|
22
|
+
interface ClientPushEvent {
|
|
23
|
+
/** Plugin-defined discriminator, e.g. `"chatMessageReceived"`. */
|
|
24
|
+
eventType: string;
|
|
25
|
+
/** Raw JSON payload authored by the plugin backend. */
|
|
26
|
+
payloadJson: string;
|
|
27
|
+
/**
|
|
28
|
+
* Host per-group monotonic sequence. Advisory only — do NOT use for
|
|
29
|
+
* cross-group gap detection (see the scope note above).
|
|
30
|
+
*/
|
|
31
|
+
eventSequence: number;
|
|
32
|
+
}
|
|
33
|
+
interface ClientPushSubscribeOptions {
|
|
34
|
+
/**
|
|
35
|
+
* Opaque group names to enrol in (e.g. `"chat:channel:{id}"`). The host
|
|
36
|
+
* authorises each subscription via the plugin's `authorize-subscription` tool
|
|
37
|
+
* and enforces org/extension isolation — a plugin cannot subscribe outside its
|
|
38
|
+
* own extension + organisation.
|
|
39
|
+
*/
|
|
40
|
+
groups: string[];
|
|
41
|
+
/** Called for each delivered (non-resync) event for the subscribed groups. */
|
|
42
|
+
onEvent: (event: ClientPushEvent) => void;
|
|
43
|
+
/**
|
|
44
|
+
* Called when the host signals a gap/resync for the subscribed groups (a
|
|
45
|
+
* dropped-event backpressure signal, or a reconnect). The consumer should
|
|
46
|
+
* re-fetch authoritative state (e.g. a delta/cold-load) rather than trusting
|
|
47
|
+
* incremental events.
|
|
48
|
+
*/
|
|
49
|
+
onResync?: () => void;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Host-provided channel for realtime server-push. The channel is already scoped to
|
|
53
|
+
* the mounted surface's extension + organisation (bound by the host from the trusted
|
|
54
|
+
* mount descriptor — a plugin CANNOT widen it), so {@link subscribe} takes only
|
|
55
|
+
* opaque group names and returns an unsubscribe function.
|
|
56
|
+
*/
|
|
57
|
+
interface ClientPushChannel {
|
|
58
|
+
subscribe(options: ClientPushSubscribeOptions): () => void;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* `null` = no host channel (standalone/mock, or a host that predates client-push) →
|
|
62
|
+
* {@link useClientPushSubscription} is inert. Provided by
|
|
63
|
+
* {@link ExtensionRuntimeProvider}'s optional `clientPush` prop.
|
|
64
|
+
*/
|
|
65
|
+
declare const ClientPushContext: react.Context<ClientPushChannel | null>;
|
|
66
|
+
interface UseClientPushSubscriptionOptions {
|
|
67
|
+
/** Opaque groups to subscribe. Changing the SET re-subscribes; identity/order changes alone do not. */
|
|
68
|
+
groups: string[];
|
|
69
|
+
onEvent: (event: ClientPushEvent) => void;
|
|
70
|
+
onResync?: () => void;
|
|
71
|
+
/** Gate the subscription (e.g. until an id is known). Default `true`. */
|
|
72
|
+
enabled?: boolean;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Subscribe a plugin surface to host client-push events for `groups`.
|
|
76
|
+
*
|
|
77
|
+
* Inert (no-op) when no host channel is present (standalone/mock), when `enabled` is
|
|
78
|
+
* false, or when `groups` is empty. Re-subscribes when the group set changes and
|
|
79
|
+
* unsubscribes on unmount. Callback identities are held in refs, so passing new
|
|
80
|
+
* inline `onEvent`/`onResync` closures every render does NOT churn the subscription.
|
|
81
|
+
*/
|
|
82
|
+
declare function useClientPushSubscription(options: UseClientPushSubscriptionOptions): void;
|
|
83
|
+
|
|
84
|
+
/** Current-user identity for a mounted plugin surface, bound by the host. */
|
|
85
|
+
interface HostIdentityUser {
|
|
86
|
+
id: string;
|
|
87
|
+
firstName: string;
|
|
88
|
+
lastName: string;
|
|
89
|
+
fullName: string;
|
|
90
|
+
isExternal: boolean;
|
|
91
|
+
}
|
|
92
|
+
/** One resolved permission grant (bitMask over the plugin's PermissionMask bits). */
|
|
93
|
+
interface HostPermission {
|
|
94
|
+
groupCode: string;
|
|
95
|
+
bitMask: number;
|
|
96
|
+
}
|
|
97
|
+
/** Host-provided identity context for the mounted surface. */
|
|
98
|
+
interface HostIdentity {
|
|
99
|
+
/** null while host auth is still loading (see isLoading) OR when unauthenticated. */
|
|
100
|
+
user: HostIdentityUser | null;
|
|
101
|
+
/** true while the host's /auth/user resolution is in flight — disambiguates loading from unauthenticated. */
|
|
102
|
+
isLoading: boolean;
|
|
103
|
+
/** The mounted surface's OWN grant only, or null when the user has no grant for this extension. */
|
|
104
|
+
permission: HostPermission | null;
|
|
105
|
+
/** The mounted surface's own extension groupCode, host-bound from the trusted manifest. */
|
|
106
|
+
extensionGroupCode: string;
|
|
107
|
+
/**
|
|
108
|
+
* The active organisation id for the mounted surface, or null while host auth is loading /
|
|
109
|
+
* unauthenticated. Host-bound from the SPA's active-organisation context. Surfaces that scope
|
|
110
|
+
* realtime subscriptions or org-keyed queries read this (e.g. the chat client-push gate); it is
|
|
111
|
+
* NOT a security token — the plugin backend derives org from the server session independently.
|
|
112
|
+
*/
|
|
113
|
+
organisationId: string | null;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* `null` = no host channel (standalone/mock, or a host that predates the identity seam) →
|
|
117
|
+
* {@link useHostIdentity} returns null and the plugin falls back to its deny-by-default path.
|
|
118
|
+
* Provided by {@link ExtensionRuntimeProvider}'s optional `identity` prop.
|
|
119
|
+
*/
|
|
120
|
+
declare const HostIdentityContext: react.Context<HostIdentity | null>;
|
|
121
|
+
/** Returns the host identity for the mounted surface, or null when no host context is present. */
|
|
122
|
+
declare function useHostIdentity(): HostIdentity | null;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* A host-supplied realtime subscription source for a plugin.
|
|
126
|
+
*
|
|
127
|
+
* The SDK keeps this intentionally dumb — it only calls `source.subscribe`.
|
|
128
|
+
* All SignalR wiring, extensionId filtering, and connection lifecycle management
|
|
129
|
+
* live host-side (Task 6 in coreconnect-web). This lets the SDK be tested with
|
|
130
|
+
* a simple fake source.
|
|
131
|
+
*/
|
|
132
|
+
interface PluginRealtimeSource {
|
|
133
|
+
/**
|
|
134
|
+
* Subscribe to notifications whose `typeCode` matches the given value.
|
|
135
|
+
*
|
|
136
|
+
* @param typeCode The application-level event type code to filter on
|
|
137
|
+
* (e.g. `"HelpdeskTicketCreated"`). Filtering by
|
|
138
|
+
* extensionId is the host's responsibility.
|
|
139
|
+
* @param handler Called with the raw notification payload whenever a
|
|
140
|
+
* matching notification arrives.
|
|
141
|
+
* @returns An unsubscribe function. Calling it removes this handler.
|
|
142
|
+
*/
|
|
143
|
+
subscribe(typeCode: string, handler: (payload: unknown) => void): () => void;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* React context carrying the plugin's active {@link PluginRealtimeSource}.
|
|
147
|
+
*
|
|
148
|
+
* `null` is the explicit "not provided" sentinel — hooks must treat null as a
|
|
149
|
+
* clean no-op (dev/mock/no-connection) rather than an error.
|
|
150
|
+
*
|
|
151
|
+
* Provided by {@link ExtensionRuntimeProvider} when the host passes a
|
|
152
|
+
* `realtime` prop; consumed by `usePluginRealtimeSource()`.
|
|
153
|
+
*/
|
|
154
|
+
declare const PluginRealtimeContext: react.Context<PluginRealtimeSource | null>;
|
|
155
|
+
/**
|
|
156
|
+
* Returns the {@link PluginRealtimeSource} from context, or `null` when none
|
|
157
|
+
* is wired (dev/mock environments, unit tests that only care about MCP).
|
|
158
|
+
*
|
|
159
|
+
* Hooks built on top of this (e.g. `usePluginRealtime` in `plugin-ui`) should
|
|
160
|
+
* skip their subscription entirely when this returns `null`.
|
|
161
|
+
*/
|
|
162
|
+
declare function usePluginRealtimeSource(): PluginRealtimeSource | null;
|
|
163
|
+
|
|
9
164
|
interface ExtensionRuntimeProviderProps {
|
|
10
165
|
transport: McpTransport;
|
|
166
|
+
/**
|
|
167
|
+
* Optional host realtime channel consumed by {@link useClientPushSubscription}.
|
|
168
|
+
* Absent (or `null`) in standalone/mock hosts and hosts that predate client-push,
|
|
169
|
+
* in which case the hook is inert. The host binds this channel to the mounted
|
|
170
|
+
* surface's trusted extension + organisation identity.
|
|
171
|
+
*/
|
|
172
|
+
clientPush?: ClientPushChannel | null;
|
|
173
|
+
/**
|
|
174
|
+
* Optional host identity/permission context consumed by {@link useHostIdentity}.
|
|
175
|
+
* Absent (or `null`) in standalone/mock hosts and hosts that predate this seam,
|
|
176
|
+
* in which case the hook is inert. The host binds this to the mounted surface's
|
|
177
|
+
* trusted extension identity and forwards only that extension's own grant.
|
|
178
|
+
*/
|
|
179
|
+
identity?: HostIdentity | null;
|
|
180
|
+
/**
|
|
181
|
+
* Optional realtime subscription source supplied by the host.
|
|
182
|
+
*
|
|
183
|
+
* When provided, descendant components can call `usePluginRealtimeSource()`
|
|
184
|
+
* to obtain it and subscribe to push notifications. When omitted (dev/mock
|
|
185
|
+
* environments or plugins that don't need realtime), the context defaults
|
|
186
|
+
* to `null` and consumers no-op cleanly.
|
|
187
|
+
*/
|
|
188
|
+
realtime?: PluginRealtimeSource;
|
|
11
189
|
children?: ReactNode;
|
|
12
190
|
}
|
|
13
191
|
/**
|
|
@@ -19,7 +197,7 @@ interface ExtensionRuntimeProviderProps {
|
|
|
19
197
|
* over the context value — useful for tests and for plugins that want to
|
|
20
198
|
* shard work across multiple hosts.
|
|
21
199
|
*/
|
|
22
|
-
declare function ExtensionRuntimeProvider({ transport, children }: ExtensionRuntimeProviderProps): ReactNode;
|
|
200
|
+
declare function ExtensionRuntimeProvider({ transport, clientPush, identity, realtime, children }: ExtensionRuntimeProviderProps): ReactNode;
|
|
23
201
|
|
|
24
202
|
interface UseMcpResourceOptions {
|
|
25
203
|
/**
|
|
@@ -445,4 +623,4 @@ interface UseFrontendSessionTokenResult {
|
|
|
445
623
|
*/
|
|
446
624
|
declare function useFrontendSessionToken(transport: McpTransport): UseFrontendSessionTokenResult;
|
|
447
625
|
|
|
448
|
-
export { BridgeClientContext, type CreatePortMcpTransportOptions, type CreateRemoteRootOptions, type DeclarativePluginConfig, type EthisysPluginConfig, ExtensionRuntimeProvider, type ExtensionRuntimeProviderProps, type ItemsResponse, LocalePayload, McpTransport, PortBridgeClient, type PortShim, type RemoteRoot, ThemePayload, type UseFrontendSessionTokenResult, type UseMcpQueryOptions, type UseMcpQueryResult, type UseMcpResourceOptions, type UseMcpResourceResult, type UseMcpToolOptions, type UseMcpToolResult, createPortMcpTransport, createRemoteRoot, defineDeclarativePlugin, defineEthisysPlugin, unwrapItems, useBridgeClient, useBridgeLocale, useBridgeTheme, useFrontendSessionToken, useMcpQuery, useMcpResource, useMcpTool };
|
|
626
|
+
export { BridgeClientContext, type ClientPushChannel, ClientPushContext, type ClientPushEvent, type ClientPushSubscribeOptions, type CreatePortMcpTransportOptions, type CreateRemoteRootOptions, type DeclarativePluginConfig, type EthisysPluginConfig, ExtensionRuntimeProvider, type ExtensionRuntimeProviderProps, type HostIdentity, HostIdentityContext, type HostIdentityUser, type HostPermission, type ItemsResponse, LocalePayload, McpTransport, PluginRealtimeContext, type PluginRealtimeSource, PortBridgeClient, type PortShim, type RemoteRoot, ThemePayload, type UseClientPushSubscriptionOptions, type UseFrontendSessionTokenResult, type UseMcpQueryOptions, type UseMcpQueryResult, type UseMcpResourceOptions, type UseMcpResourceResult, type UseMcpToolOptions, type UseMcpToolResult, createPortMcpTransport, createRemoteRoot, defineDeclarativePlugin, defineEthisysPlugin, unwrapItems, useBridgeClient, useBridgeLocale, useBridgeTheme, useClientPushSubscription, useFrontendSessionToken, useHostIdentity, useMcpQuery, useMcpResource, useMcpTool, usePluginRealtimeSource };
|
package/dist/plugin/index.d.ts
CHANGED
|
@@ -6,8 +6,186 @@ import { SduiNode, RenderMode } from '@ethisyscore/protocol';
|
|
|
6
6
|
import { RemoteConnection } from '@remote-dom/core';
|
|
7
7
|
import '../bridge-envelopes-BRKGSiSC.js';
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* A single client-push event delivered from the host to a plugin surface. The host
|
|
11
|
+
* relays the plugin backend's `IClientPushPublisher` events over its realtime
|
|
12
|
+
* channel (SignalR); the transport envelope's extension identity is bound by the
|
|
13
|
+
* host at mount time, so the plugin sees only the event body.
|
|
14
|
+
*
|
|
15
|
+
* SCOPE + ORDERING: which channel/user/group an event concerns is carried INSIDE
|
|
16
|
+
* `payloadJson` by the emitting plugin — the transport envelope intentionally has no
|
|
17
|
+
* group field. Consumers therefore demultiplex + order by their own payload fields
|
|
18
|
+
* (e.g. a per-channel sequence in the payload), NOT by {@link eventSequence}, which
|
|
19
|
+
* is per-group at the host and would produce false gaps when multiple groups
|
|
20
|
+
* multiplex over one connection.
|
|
21
|
+
*/
|
|
22
|
+
interface ClientPushEvent {
|
|
23
|
+
/** Plugin-defined discriminator, e.g. `"chatMessageReceived"`. */
|
|
24
|
+
eventType: string;
|
|
25
|
+
/** Raw JSON payload authored by the plugin backend. */
|
|
26
|
+
payloadJson: string;
|
|
27
|
+
/**
|
|
28
|
+
* Host per-group monotonic sequence. Advisory only — do NOT use for
|
|
29
|
+
* cross-group gap detection (see the scope note above).
|
|
30
|
+
*/
|
|
31
|
+
eventSequence: number;
|
|
32
|
+
}
|
|
33
|
+
interface ClientPushSubscribeOptions {
|
|
34
|
+
/**
|
|
35
|
+
* Opaque group names to enrol in (e.g. `"chat:channel:{id}"`). The host
|
|
36
|
+
* authorises each subscription via the plugin's `authorize-subscription` tool
|
|
37
|
+
* and enforces org/extension isolation — a plugin cannot subscribe outside its
|
|
38
|
+
* own extension + organisation.
|
|
39
|
+
*/
|
|
40
|
+
groups: string[];
|
|
41
|
+
/** Called for each delivered (non-resync) event for the subscribed groups. */
|
|
42
|
+
onEvent: (event: ClientPushEvent) => void;
|
|
43
|
+
/**
|
|
44
|
+
* Called when the host signals a gap/resync for the subscribed groups (a
|
|
45
|
+
* dropped-event backpressure signal, or a reconnect). The consumer should
|
|
46
|
+
* re-fetch authoritative state (e.g. a delta/cold-load) rather than trusting
|
|
47
|
+
* incremental events.
|
|
48
|
+
*/
|
|
49
|
+
onResync?: () => void;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Host-provided channel for realtime server-push. The channel is already scoped to
|
|
53
|
+
* the mounted surface's extension + organisation (bound by the host from the trusted
|
|
54
|
+
* mount descriptor — a plugin CANNOT widen it), so {@link subscribe} takes only
|
|
55
|
+
* opaque group names and returns an unsubscribe function.
|
|
56
|
+
*/
|
|
57
|
+
interface ClientPushChannel {
|
|
58
|
+
subscribe(options: ClientPushSubscribeOptions): () => void;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* `null` = no host channel (standalone/mock, or a host that predates client-push) →
|
|
62
|
+
* {@link useClientPushSubscription} is inert. Provided by
|
|
63
|
+
* {@link ExtensionRuntimeProvider}'s optional `clientPush` prop.
|
|
64
|
+
*/
|
|
65
|
+
declare const ClientPushContext: react.Context<ClientPushChannel | null>;
|
|
66
|
+
interface UseClientPushSubscriptionOptions {
|
|
67
|
+
/** Opaque groups to subscribe. Changing the SET re-subscribes; identity/order changes alone do not. */
|
|
68
|
+
groups: string[];
|
|
69
|
+
onEvent: (event: ClientPushEvent) => void;
|
|
70
|
+
onResync?: () => void;
|
|
71
|
+
/** Gate the subscription (e.g. until an id is known). Default `true`. */
|
|
72
|
+
enabled?: boolean;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Subscribe a plugin surface to host client-push events for `groups`.
|
|
76
|
+
*
|
|
77
|
+
* Inert (no-op) when no host channel is present (standalone/mock), when `enabled` is
|
|
78
|
+
* false, or when `groups` is empty. Re-subscribes when the group set changes and
|
|
79
|
+
* unsubscribes on unmount. Callback identities are held in refs, so passing new
|
|
80
|
+
* inline `onEvent`/`onResync` closures every render does NOT churn the subscription.
|
|
81
|
+
*/
|
|
82
|
+
declare function useClientPushSubscription(options: UseClientPushSubscriptionOptions): void;
|
|
83
|
+
|
|
84
|
+
/** Current-user identity for a mounted plugin surface, bound by the host. */
|
|
85
|
+
interface HostIdentityUser {
|
|
86
|
+
id: string;
|
|
87
|
+
firstName: string;
|
|
88
|
+
lastName: string;
|
|
89
|
+
fullName: string;
|
|
90
|
+
isExternal: boolean;
|
|
91
|
+
}
|
|
92
|
+
/** One resolved permission grant (bitMask over the plugin's PermissionMask bits). */
|
|
93
|
+
interface HostPermission {
|
|
94
|
+
groupCode: string;
|
|
95
|
+
bitMask: number;
|
|
96
|
+
}
|
|
97
|
+
/** Host-provided identity context for the mounted surface. */
|
|
98
|
+
interface HostIdentity {
|
|
99
|
+
/** null while host auth is still loading (see isLoading) OR when unauthenticated. */
|
|
100
|
+
user: HostIdentityUser | null;
|
|
101
|
+
/** true while the host's /auth/user resolution is in flight — disambiguates loading from unauthenticated. */
|
|
102
|
+
isLoading: boolean;
|
|
103
|
+
/** The mounted surface's OWN grant only, or null when the user has no grant for this extension. */
|
|
104
|
+
permission: HostPermission | null;
|
|
105
|
+
/** The mounted surface's own extension groupCode, host-bound from the trusted manifest. */
|
|
106
|
+
extensionGroupCode: string;
|
|
107
|
+
/**
|
|
108
|
+
* The active organisation id for the mounted surface, or null while host auth is loading /
|
|
109
|
+
* unauthenticated. Host-bound from the SPA's active-organisation context. Surfaces that scope
|
|
110
|
+
* realtime subscriptions or org-keyed queries read this (e.g. the chat client-push gate); it is
|
|
111
|
+
* NOT a security token — the plugin backend derives org from the server session independently.
|
|
112
|
+
*/
|
|
113
|
+
organisationId: string | null;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* `null` = no host channel (standalone/mock, or a host that predates the identity seam) →
|
|
117
|
+
* {@link useHostIdentity} returns null and the plugin falls back to its deny-by-default path.
|
|
118
|
+
* Provided by {@link ExtensionRuntimeProvider}'s optional `identity` prop.
|
|
119
|
+
*/
|
|
120
|
+
declare const HostIdentityContext: react.Context<HostIdentity | null>;
|
|
121
|
+
/** Returns the host identity for the mounted surface, or null when no host context is present. */
|
|
122
|
+
declare function useHostIdentity(): HostIdentity | null;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* A host-supplied realtime subscription source for a plugin.
|
|
126
|
+
*
|
|
127
|
+
* The SDK keeps this intentionally dumb — it only calls `source.subscribe`.
|
|
128
|
+
* All SignalR wiring, extensionId filtering, and connection lifecycle management
|
|
129
|
+
* live host-side (Task 6 in coreconnect-web). This lets the SDK be tested with
|
|
130
|
+
* a simple fake source.
|
|
131
|
+
*/
|
|
132
|
+
interface PluginRealtimeSource {
|
|
133
|
+
/**
|
|
134
|
+
* Subscribe to notifications whose `typeCode` matches the given value.
|
|
135
|
+
*
|
|
136
|
+
* @param typeCode The application-level event type code to filter on
|
|
137
|
+
* (e.g. `"HelpdeskTicketCreated"`). Filtering by
|
|
138
|
+
* extensionId is the host's responsibility.
|
|
139
|
+
* @param handler Called with the raw notification payload whenever a
|
|
140
|
+
* matching notification arrives.
|
|
141
|
+
* @returns An unsubscribe function. Calling it removes this handler.
|
|
142
|
+
*/
|
|
143
|
+
subscribe(typeCode: string, handler: (payload: unknown) => void): () => void;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* React context carrying the plugin's active {@link PluginRealtimeSource}.
|
|
147
|
+
*
|
|
148
|
+
* `null` is the explicit "not provided" sentinel — hooks must treat null as a
|
|
149
|
+
* clean no-op (dev/mock/no-connection) rather than an error.
|
|
150
|
+
*
|
|
151
|
+
* Provided by {@link ExtensionRuntimeProvider} when the host passes a
|
|
152
|
+
* `realtime` prop; consumed by `usePluginRealtimeSource()`.
|
|
153
|
+
*/
|
|
154
|
+
declare const PluginRealtimeContext: react.Context<PluginRealtimeSource | null>;
|
|
155
|
+
/**
|
|
156
|
+
* Returns the {@link PluginRealtimeSource} from context, or `null` when none
|
|
157
|
+
* is wired (dev/mock environments, unit tests that only care about MCP).
|
|
158
|
+
*
|
|
159
|
+
* Hooks built on top of this (e.g. `usePluginRealtime` in `plugin-ui`) should
|
|
160
|
+
* skip their subscription entirely when this returns `null`.
|
|
161
|
+
*/
|
|
162
|
+
declare function usePluginRealtimeSource(): PluginRealtimeSource | null;
|
|
163
|
+
|
|
9
164
|
interface ExtensionRuntimeProviderProps {
|
|
10
165
|
transport: McpTransport;
|
|
166
|
+
/**
|
|
167
|
+
* Optional host realtime channel consumed by {@link useClientPushSubscription}.
|
|
168
|
+
* Absent (or `null`) in standalone/mock hosts and hosts that predate client-push,
|
|
169
|
+
* in which case the hook is inert. The host binds this channel to the mounted
|
|
170
|
+
* surface's trusted extension + organisation identity.
|
|
171
|
+
*/
|
|
172
|
+
clientPush?: ClientPushChannel | null;
|
|
173
|
+
/**
|
|
174
|
+
* Optional host identity/permission context consumed by {@link useHostIdentity}.
|
|
175
|
+
* Absent (or `null`) in standalone/mock hosts and hosts that predate this seam,
|
|
176
|
+
* in which case the hook is inert. The host binds this to the mounted surface's
|
|
177
|
+
* trusted extension identity and forwards only that extension's own grant.
|
|
178
|
+
*/
|
|
179
|
+
identity?: HostIdentity | null;
|
|
180
|
+
/**
|
|
181
|
+
* Optional realtime subscription source supplied by the host.
|
|
182
|
+
*
|
|
183
|
+
* When provided, descendant components can call `usePluginRealtimeSource()`
|
|
184
|
+
* to obtain it and subscribe to push notifications. When omitted (dev/mock
|
|
185
|
+
* environments or plugins that don't need realtime), the context defaults
|
|
186
|
+
* to `null` and consumers no-op cleanly.
|
|
187
|
+
*/
|
|
188
|
+
realtime?: PluginRealtimeSource;
|
|
11
189
|
children?: ReactNode;
|
|
12
190
|
}
|
|
13
191
|
/**
|
|
@@ -19,7 +197,7 @@ interface ExtensionRuntimeProviderProps {
|
|
|
19
197
|
* over the context value — useful for tests and for plugins that want to
|
|
20
198
|
* shard work across multiple hosts.
|
|
21
199
|
*/
|
|
22
|
-
declare function ExtensionRuntimeProvider({ transport, children }: ExtensionRuntimeProviderProps): ReactNode;
|
|
200
|
+
declare function ExtensionRuntimeProvider({ transport, clientPush, identity, realtime, children }: ExtensionRuntimeProviderProps): ReactNode;
|
|
23
201
|
|
|
24
202
|
interface UseMcpResourceOptions {
|
|
25
203
|
/**
|
|
@@ -445,4 +623,4 @@ interface UseFrontendSessionTokenResult {
|
|
|
445
623
|
*/
|
|
446
624
|
declare function useFrontendSessionToken(transport: McpTransport): UseFrontendSessionTokenResult;
|
|
447
625
|
|
|
448
|
-
export { BridgeClientContext, type CreatePortMcpTransportOptions, type CreateRemoteRootOptions, type DeclarativePluginConfig, type EthisysPluginConfig, ExtensionRuntimeProvider, type ExtensionRuntimeProviderProps, type ItemsResponse, LocalePayload, McpTransport, PortBridgeClient, type PortShim, type RemoteRoot, ThemePayload, type UseFrontendSessionTokenResult, type UseMcpQueryOptions, type UseMcpQueryResult, type UseMcpResourceOptions, type UseMcpResourceResult, type UseMcpToolOptions, type UseMcpToolResult, createPortMcpTransport, createRemoteRoot, defineDeclarativePlugin, defineEthisysPlugin, unwrapItems, useBridgeClient, useBridgeLocale, useBridgeTheme, useFrontendSessionToken, useMcpQuery, useMcpResource, useMcpTool };
|
|
626
|
+
export { BridgeClientContext, type ClientPushChannel, ClientPushContext, type ClientPushEvent, type ClientPushSubscribeOptions, type CreatePortMcpTransportOptions, type CreateRemoteRootOptions, type DeclarativePluginConfig, type EthisysPluginConfig, ExtensionRuntimeProvider, type ExtensionRuntimeProviderProps, type HostIdentity, HostIdentityContext, type HostIdentityUser, type HostPermission, type ItemsResponse, LocalePayload, McpTransport, PluginRealtimeContext, type PluginRealtimeSource, PortBridgeClient, type PortShim, type RemoteRoot, ThemePayload, type UseClientPushSubscriptionOptions, type UseFrontendSessionTokenResult, type UseMcpQueryOptions, type UseMcpQueryResult, type UseMcpResourceOptions, type UseMcpResourceResult, type UseMcpToolOptions, type UseMcpToolResult, createPortMcpTransport, createRemoteRoot, defineDeclarativePlugin, defineEthisysPlugin, unwrapItems, useBridgeClient, useBridgeLocale, useBridgeTheme, useClientPushSubscription, useFrontendSessionToken, useHostIdentity, useMcpQuery, useMcpResource, useMcpTool, usePluginRealtimeSource };
|
package/dist/plugin/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createContext,
|
|
1
|
+
import { createContext, useContext, useRef, useEffect, useMemo, useState, useCallback } from 'react';
|
|
2
2
|
import { jsx } from 'react/jsx-runtime';
|
|
3
3
|
import { BatchingRemoteConnection } from '@remote-dom/core/elements';
|
|
4
4
|
import ReactReconciler from 'react-reconciler';
|
|
@@ -6,10 +6,43 @@ import { LegacyRoot, DefaultEventPriority } from 'react-reconciler/constants.js'
|
|
|
6
6
|
import { MUTATION_TYPE_UPDATE_TEXT, MUTATION_TYPE_UPDATE_PROPERTY, MUTATION_TYPE_INSERT_CHILD, MUTATION_TYPE_REMOVE_CHILD, NODE_TYPE_TEXT, NODE_TYPE_ELEMENT, ROOT_ID } from '@remote-dom/core';
|
|
7
7
|
|
|
8
8
|
// src/plugin/ExtensionRuntimeProvider.tsx
|
|
9
|
+
var ClientPushContext = createContext(null);
|
|
10
|
+
function useClientPushSubscription(options) {
|
|
11
|
+
const { groups, onEvent, onResync, enabled = true } = options;
|
|
12
|
+
const channel = useContext(ClientPushContext);
|
|
13
|
+
const onEventRef = useRef(onEvent);
|
|
14
|
+
onEventRef.current = onEvent;
|
|
15
|
+
const onResyncRef = useRef(onResync);
|
|
16
|
+
onResyncRef.current = onResync;
|
|
17
|
+
const normalizedGroups = [...new Set(groups)].sort();
|
|
18
|
+
const groupsKey = JSON.stringify(normalizedGroups);
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (!channel || !enabled || normalizedGroups.length === 0) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const unsubscribe = channel.subscribe({
|
|
24
|
+
groups: normalizedGroups,
|
|
25
|
+
onEvent: (event) => onEventRef.current(event),
|
|
26
|
+
onResync: () => onResyncRef.current?.()
|
|
27
|
+
});
|
|
28
|
+
return unsubscribe;
|
|
29
|
+
}, [channel, enabled, groupsKey]);
|
|
30
|
+
}
|
|
31
|
+
var HostIdentityContext = createContext(null);
|
|
32
|
+
function useHostIdentity() {
|
|
33
|
+
return useContext(HostIdentityContext);
|
|
34
|
+
}
|
|
35
|
+
var PluginRealtimeContext = createContext(null);
|
|
36
|
+
function usePluginRealtimeSource() {
|
|
37
|
+
return useContext(PluginRealtimeContext);
|
|
38
|
+
}
|
|
9
39
|
var ExtensionRuntimeContext = createContext(null);
|
|
10
|
-
function ExtensionRuntimeProvider({ transport, children }) {
|
|
40
|
+
function ExtensionRuntimeProvider({ transport, clientPush = null, identity = null, realtime, children }) {
|
|
11
41
|
const value = useMemo(() => transport, [transport]);
|
|
12
|
-
|
|
42
|
+
const push = useMemo(() => clientPush, [clientPush]);
|
|
43
|
+
const id = useMemo(() => identity, [identity]);
|
|
44
|
+
const realtimeValue = useMemo(() => realtime ?? null, [realtime]);
|
|
45
|
+
return /* @__PURE__ */ jsx(ExtensionRuntimeContext.Provider, { value, children: /* @__PURE__ */ jsx(ClientPushContext.Provider, { value: push, children: /* @__PURE__ */ jsx(HostIdentityContext.Provider, { value: id, children: /* @__PURE__ */ jsx(PluginRealtimeContext.Provider, { value: realtimeValue, children }) }) }) });
|
|
13
46
|
}
|
|
14
47
|
function useExtensionRuntimeTransport(override) {
|
|
15
48
|
const fromContext = useContext(ExtensionRuntimeContext);
|
|
@@ -718,6 +751,6 @@ function useFrontendSessionToken(transport) {
|
|
|
718
751
|
return { token, isLoading, error };
|
|
719
752
|
}
|
|
720
753
|
|
|
721
|
-
export { BridgeClientContext, ExtensionRuntimeProvider, createPortBridgeClient, createPortMcpTransport, createRemoteRoot, defineDeclarativePlugin, defineEthisysPlugin, unwrapItems, useBridgeClient, useBridgeLocale, useBridgeTheme, useFrontendSessionToken, useMcpQuery, useMcpResource, useMcpTool };
|
|
754
|
+
export { BridgeClientContext, ClientPushContext, ExtensionRuntimeProvider, HostIdentityContext, PluginRealtimeContext, createPortBridgeClient, createPortMcpTransport, createRemoteRoot, defineDeclarativePlugin, defineEthisysPlugin, unwrapItems, useBridgeClient, useBridgeLocale, useBridgeTheme, useClientPushSubscription, useFrontendSessionToken, useHostIdentity, useMcpQuery, useMcpResource, useMcpTool, usePluginRealtimeSource };
|
|
722
755
|
//# sourceMappingURL=index.js.map
|
|
723
756
|
//# sourceMappingURL=index.js.map
|