@ethisyscore/extension-runtime 1.58.0 → 1.61.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.
@@ -1,5 +1,94 @@
1
1
  import { U as UploadDocumentMeta, d as BridgePushThemeEnvelope, c as BridgePushLocaleEnvelope, b as BridgePushDensityEnvelope, a as BridgePushA11yEnvelope, B as BridgeNavPushEnvelope } from './bridge-envelopes-DA6vxbyb.js';
2
2
 
3
+ /**
4
+ * Version-skew detection for host-side MCP calls.
5
+ *
6
+ * A mounted plugin surface holds a capability token minted against the extension version that
7
+ * was active when the page loaded. When the install is upgraded underneath it, the kernel stops
8
+ * honouring that token: it rejects with `401` and
9
+ * `X-Capability-Token-Rejection-Reason: ExtensionVersionMismatch`. Every other rejection reason is
10
+ * terminal for the surface, but this one is recoverable — remounting against the new version fixes
11
+ * it — which is why it is worth telling apart from an ordinary auth failure.
12
+ *
13
+ * Two signals are handled here, and the difference matters:
14
+ *
15
+ * - **Reactive**, from a rejection: the call already failed. Recovery costs the user a round trip
16
+ * and whatever in-page state the remount discards.
17
+ * - **Proactive**, from the version stamped on a *successful* response: the surface learns the
18
+ * install moved while it is still working, and can remount at a moment of its choosing. Reads
19
+ * keep succeeding until the token's own version check catches up, so this is the path that
20
+ * avoids a visible failure altogether.
21
+ *
22
+ * The interpretation lives here rather than in each host application so that "which reason means
23
+ * remount" is decided once, in code that can be tested, instead of being re-derived by every
24
+ * embedder from HTTP trivia.
25
+ */
26
+ /** Response header naming the extension version a request was served as. */
27
+ declare const EXTENSION_VERSION_HEADER = "x-extension-version";
28
+ /** Response header naming the version a rejected surface must remount at. */
29
+ declare const EXTENSION_ACTIVE_VERSION_HEADER = "x-extension-active-version";
30
+ /** Response header carrying the kernel's structured capability-token rejection reason. */
31
+ declare const CAPABILITY_REJECTION_REASON_HEADER = "x-capability-token-rejection-reason";
32
+ /** The one rejection reason a surface can resolve by remounting. */
33
+ declare const VERSION_MISMATCH_REASON = "extensionversionmismatch";
34
+ /**
35
+ * What the host observed about an MCP response, beyond its payload. Both fields are optional
36
+ * because the {@link McpHttpClient} contract predates them: an implementation that does not supply
37
+ * them simply never reports skew, which is the previous behaviour rather than a new failure.
38
+ */
39
+ interface McpResponseMetadata {
40
+ /** HTTP status code, when the implementation surfaces it. */
41
+ readonly status?: number;
42
+ /** Response headers. Looked up case-insensitively, so any casing is fine. */
43
+ readonly headers?: Readonly<Record<string, string>>;
44
+ }
45
+ /**
46
+ * How skew came to light.
47
+ *
48
+ * - `"rejection"` — a call was refused because the surface's token is stale. Recovery costs a round
49
+ * trip and whatever in-page state the remount discards.
50
+ * - `"response-stamp"` — a call succeeded, but the install has moved on. The surface can remount
51
+ * before anything fails, so this is the path worth preferring.
52
+ */
53
+ type VersionSkewSource = "rejection" | "response-stamp";
54
+ /**
55
+ * The result of one host-side MCP call: the payload the plugin asked for, plus whatever the host
56
+ * observed about the response itself.
57
+ *
58
+ * Named rather than inlined at the {@link McpHttpClient} declaration so hosts have a type to
59
+ * implement against, and so the skew detector and the transports agree on one shape instead of
60
+ * three structurally-compatible copies drifting apart.
61
+ */
62
+ interface McpHttpResponse extends McpResponseMetadata {
63
+ /** Whether the call succeeded. */
64
+ readonly ok: boolean;
65
+ /** The response payload, shaped by the request kind. */
66
+ readonly data?: unknown;
67
+ /** Failure message when {@link ok} is false. */
68
+ readonly error?: string;
69
+ }
70
+ /** A detected mismatch between the version a surface is running and the version now installed. */
71
+ interface VersionSkew {
72
+ /**
73
+ * The version to remount at, when the kernel named it. Undefined means skew is certain but the
74
+ * target is not — the surface should re-read the extension descriptor rather than guess.
75
+ */
76
+ readonly activeVersion?: string;
77
+ /** Which signal detected the skew. */
78
+ readonly detectedFrom: VersionSkewSource;
79
+ }
80
+ /**
81
+ * Decides whether an MCP response means the surface is running against a version that is no longer
82
+ * installed.
83
+ *
84
+ * @param response What the host observed: whether the call succeeded, plus any status and headers.
85
+ * @param mountedExtensionVersion The version this surface mounted at. Required for proactive
86
+ * detection — without it a response stamp cannot be compared to anything, and only rejections are
87
+ * detectable.
88
+ * @returns The skew, or `null` when the response says nothing about versions.
89
+ */
90
+ declare function detectVersionSkew(response: McpHttpResponse, mountedExtensionVersion?: string): VersionSkew | null;
91
+
3
92
  /**
4
93
  * Worker-side host transport for `renderMode: remote-runtime` extensions
5
94
  * (Contract B). Owns the lifecycle of:
@@ -80,11 +169,12 @@ interface McpUploadRequest {
80
169
  * branch until updated (the FE upload hook is inert until the host handles it).
81
170
  */
82
171
  interface McpHttpClient {
83
- fetch(request: McpHttpRequest): Promise<{
84
- ok: boolean;
85
- data?: unknown;
86
- error?: string;
87
- }>;
172
+ /**
173
+ * Performs the call. The response's `status` and `headers` are optional and additive: supplying
174
+ * them is what enables version-skew detection (see {@link detectVersionSkew}), and an
175
+ * implementation that omits them behaves exactly as before rather than failing.
176
+ */
177
+ fetch(request: McpHttpRequest): Promise<McpHttpResponse>;
88
178
  }
89
179
  /**
90
180
  * Construction options for {@link WorkerRemoteDomTransport}.
@@ -108,6 +198,20 @@ interface WorkerRemoteDomTransportOptions {
108
198
  * back through the port.
109
199
  */
110
200
  mcpClient: McpHttpClient;
201
+ /**
202
+ * The extension version this surface mounted at. Supplying it enables *proactive* skew
203
+ * detection: a successful response stamped with a different version means the install moved
204
+ * while the surface is still working, so it can remount before anything fails. Without it only
205
+ * outright rejections are detectable, which is a worse experience but not a broken one.
206
+ */
207
+ mountedExtensionVersion?: string;
208
+ /**
209
+ * Called at most once, when this surface is found to be running against a version that is no
210
+ * longer installed. The host is expected to remount the surface (which re-mints a capability
211
+ * token against the new version); the transport itself does not retry, because the pending call
212
+ * belongs to a page that is about to be replaced.
213
+ */
214
+ onVersionSkew?: (skew: VersionSkew) => void;
111
215
  /**
112
216
  * Injectable Worker constructor (tests use a fake). Defaults to the
113
217
  * global `Worker`.
@@ -216,6 +320,9 @@ declare class WorkerRemoteDomTransport {
216
320
  private readonly workerPort;
217
321
  private readonly mcpClient;
218
322
  private readonly capabilityTokenProvider;
323
+ private readonly mountedExtensionVersion?;
324
+ private readonly onVersionSkew?;
325
+ private versionSkewReported;
219
326
  private readonly coalesceMs;
220
327
  private readonly maxConcurrentMcpRequests;
221
328
  private readonly abortController;
@@ -357,10 +464,25 @@ declare class WorkerRemoteDomTransport {
357
464
  * to the dispose-level controller.
358
465
  */
359
466
  private requestSignal;
467
+ /**
468
+ * Raises the skew callback the first time this surface is seen to be running against a version
469
+ * that is no longer installed.
470
+ *
471
+ * Latched deliberately. A page typically has several MCP calls in flight, and after an upgrade
472
+ * every one of them reports the same skew — firing per response would ask the host to remount
473
+ * the same surface repeatedly, and the second remount would tear down the first. One signal per
474
+ * transport is all a remount needs, and the transport is discarded along with the surface.
475
+ *
476
+ * The callback is host code, so it is assumed to be able to throw. Two consequences are guarded
477
+ * here: the throw must not escape into the MCP handler's catch (which would reply `ok: false`
478
+ * for a call that actually succeeded), and it must not consume the latch (which would leave the
479
+ * surface with no response AND no further chance of being told to remount).
480
+ */
481
+ private reportVersionSkew;
360
482
  private handleInvokeTool;
361
483
  private handleGetResource;
362
484
  private handleUploadDocument;
363
485
  private replyError;
364
486
  }
365
487
 
366
- export { DEFAULT_MAX_CONCURRENT_MCP_REQUESTS as D, type InputEventPayload as I, type McpHttpClient as M, WORKER_TRANSPORT_PROTOCOL as W, type McpHttpRequest as a, type McpUploadRequest as b, type WorkerCtor as c, type WorkerHandshakePayload as d, type WorkerLike as e, WorkerRemoteDomTransport as f, type WorkerRemoteDomTransportOptions as g };
488
+ export { CAPABILITY_REJECTION_REASON_HEADER as C, DEFAULT_MAX_CONCURRENT_MCP_REQUESTS as D, EXTENSION_ACTIVE_VERSION_HEADER as E, type InputEventPayload as I, type McpHttpClient as M, VERSION_MISMATCH_REASON as V, WORKER_TRANSPORT_PROTOCOL as W, EXTENSION_VERSION_HEADER as a, type McpHttpRequest as b, type McpHttpResponse as c, type McpResponseMetadata as d, type McpUploadRequest as e, type VersionSkew as f, type VersionSkewSource as g, type WorkerCtor as h, type WorkerHandshakePayload as i, type WorkerLike as j, WorkerRemoteDomTransport as k, type WorkerRemoteDomTransportOptions as l, detectVersionSkew as m };
@@ -1,5 +1,94 @@
1
1
  import { U as UploadDocumentMeta, d as BridgePushThemeEnvelope, c as BridgePushLocaleEnvelope, b as BridgePushDensityEnvelope, a as BridgePushA11yEnvelope, B as BridgeNavPushEnvelope } from './bridge-envelopes-DA6vxbyb.cjs';
2
2
 
3
+ /**
4
+ * Version-skew detection for host-side MCP calls.
5
+ *
6
+ * A mounted plugin surface holds a capability token minted against the extension version that
7
+ * was active when the page loaded. When the install is upgraded underneath it, the kernel stops
8
+ * honouring that token: it rejects with `401` and
9
+ * `X-Capability-Token-Rejection-Reason: ExtensionVersionMismatch`. Every other rejection reason is
10
+ * terminal for the surface, but this one is recoverable — remounting against the new version fixes
11
+ * it — which is why it is worth telling apart from an ordinary auth failure.
12
+ *
13
+ * Two signals are handled here, and the difference matters:
14
+ *
15
+ * - **Reactive**, from a rejection: the call already failed. Recovery costs the user a round trip
16
+ * and whatever in-page state the remount discards.
17
+ * - **Proactive**, from the version stamped on a *successful* response: the surface learns the
18
+ * install moved while it is still working, and can remount at a moment of its choosing. Reads
19
+ * keep succeeding until the token's own version check catches up, so this is the path that
20
+ * avoids a visible failure altogether.
21
+ *
22
+ * The interpretation lives here rather than in each host application so that "which reason means
23
+ * remount" is decided once, in code that can be tested, instead of being re-derived by every
24
+ * embedder from HTTP trivia.
25
+ */
26
+ /** Response header naming the extension version a request was served as. */
27
+ declare const EXTENSION_VERSION_HEADER = "x-extension-version";
28
+ /** Response header naming the version a rejected surface must remount at. */
29
+ declare const EXTENSION_ACTIVE_VERSION_HEADER = "x-extension-active-version";
30
+ /** Response header carrying the kernel's structured capability-token rejection reason. */
31
+ declare const CAPABILITY_REJECTION_REASON_HEADER = "x-capability-token-rejection-reason";
32
+ /** The one rejection reason a surface can resolve by remounting. */
33
+ declare const VERSION_MISMATCH_REASON = "extensionversionmismatch";
34
+ /**
35
+ * What the host observed about an MCP response, beyond its payload. Both fields are optional
36
+ * because the {@link McpHttpClient} contract predates them: an implementation that does not supply
37
+ * them simply never reports skew, which is the previous behaviour rather than a new failure.
38
+ */
39
+ interface McpResponseMetadata {
40
+ /** HTTP status code, when the implementation surfaces it. */
41
+ readonly status?: number;
42
+ /** Response headers. Looked up case-insensitively, so any casing is fine. */
43
+ readonly headers?: Readonly<Record<string, string>>;
44
+ }
45
+ /**
46
+ * How skew came to light.
47
+ *
48
+ * - `"rejection"` — a call was refused because the surface's token is stale. Recovery costs a round
49
+ * trip and whatever in-page state the remount discards.
50
+ * - `"response-stamp"` — a call succeeded, but the install has moved on. The surface can remount
51
+ * before anything fails, so this is the path worth preferring.
52
+ */
53
+ type VersionSkewSource = "rejection" | "response-stamp";
54
+ /**
55
+ * The result of one host-side MCP call: the payload the plugin asked for, plus whatever the host
56
+ * observed about the response itself.
57
+ *
58
+ * Named rather than inlined at the {@link McpHttpClient} declaration so hosts have a type to
59
+ * implement against, and so the skew detector and the transports agree on one shape instead of
60
+ * three structurally-compatible copies drifting apart.
61
+ */
62
+ interface McpHttpResponse extends McpResponseMetadata {
63
+ /** Whether the call succeeded. */
64
+ readonly ok: boolean;
65
+ /** The response payload, shaped by the request kind. */
66
+ readonly data?: unknown;
67
+ /** Failure message when {@link ok} is false. */
68
+ readonly error?: string;
69
+ }
70
+ /** A detected mismatch between the version a surface is running and the version now installed. */
71
+ interface VersionSkew {
72
+ /**
73
+ * The version to remount at, when the kernel named it. Undefined means skew is certain but the
74
+ * target is not — the surface should re-read the extension descriptor rather than guess.
75
+ */
76
+ readonly activeVersion?: string;
77
+ /** Which signal detected the skew. */
78
+ readonly detectedFrom: VersionSkewSource;
79
+ }
80
+ /**
81
+ * Decides whether an MCP response means the surface is running against a version that is no longer
82
+ * installed.
83
+ *
84
+ * @param response What the host observed: whether the call succeeded, plus any status and headers.
85
+ * @param mountedExtensionVersion The version this surface mounted at. Required for proactive
86
+ * detection — without it a response stamp cannot be compared to anything, and only rejections are
87
+ * detectable.
88
+ * @returns The skew, or `null` when the response says nothing about versions.
89
+ */
90
+ declare function detectVersionSkew(response: McpHttpResponse, mountedExtensionVersion?: string): VersionSkew | null;
91
+
3
92
  /**
4
93
  * Worker-side host transport for `renderMode: remote-runtime` extensions
5
94
  * (Contract B). Owns the lifecycle of:
@@ -80,11 +169,12 @@ interface McpUploadRequest {
80
169
  * branch until updated (the FE upload hook is inert until the host handles it).
81
170
  */
82
171
  interface McpHttpClient {
83
- fetch(request: McpHttpRequest): Promise<{
84
- ok: boolean;
85
- data?: unknown;
86
- error?: string;
87
- }>;
172
+ /**
173
+ * Performs the call. The response's `status` and `headers` are optional and additive: supplying
174
+ * them is what enables version-skew detection (see {@link detectVersionSkew}), and an
175
+ * implementation that omits them behaves exactly as before rather than failing.
176
+ */
177
+ fetch(request: McpHttpRequest): Promise<McpHttpResponse>;
88
178
  }
89
179
  /**
90
180
  * Construction options for {@link WorkerRemoteDomTransport}.
@@ -108,6 +198,20 @@ interface WorkerRemoteDomTransportOptions {
108
198
  * back through the port.
109
199
  */
110
200
  mcpClient: McpHttpClient;
201
+ /**
202
+ * The extension version this surface mounted at. Supplying it enables *proactive* skew
203
+ * detection: a successful response stamped with a different version means the install moved
204
+ * while the surface is still working, so it can remount before anything fails. Without it only
205
+ * outright rejections are detectable, which is a worse experience but not a broken one.
206
+ */
207
+ mountedExtensionVersion?: string;
208
+ /**
209
+ * Called at most once, when this surface is found to be running against a version that is no
210
+ * longer installed. The host is expected to remount the surface (which re-mints a capability
211
+ * token against the new version); the transport itself does not retry, because the pending call
212
+ * belongs to a page that is about to be replaced.
213
+ */
214
+ onVersionSkew?: (skew: VersionSkew) => void;
111
215
  /**
112
216
  * Injectable Worker constructor (tests use a fake). Defaults to the
113
217
  * global `Worker`.
@@ -216,6 +320,9 @@ declare class WorkerRemoteDomTransport {
216
320
  private readonly workerPort;
217
321
  private readonly mcpClient;
218
322
  private readonly capabilityTokenProvider;
323
+ private readonly mountedExtensionVersion?;
324
+ private readonly onVersionSkew?;
325
+ private versionSkewReported;
219
326
  private readonly coalesceMs;
220
327
  private readonly maxConcurrentMcpRequests;
221
328
  private readonly abortController;
@@ -357,10 +464,25 @@ declare class WorkerRemoteDomTransport {
357
464
  * to the dispose-level controller.
358
465
  */
359
466
  private requestSignal;
467
+ /**
468
+ * Raises the skew callback the first time this surface is seen to be running against a version
469
+ * that is no longer installed.
470
+ *
471
+ * Latched deliberately. A page typically has several MCP calls in flight, and after an upgrade
472
+ * every one of them reports the same skew — firing per response would ask the host to remount
473
+ * the same surface repeatedly, and the second remount would tear down the first. One signal per
474
+ * transport is all a remount needs, and the transport is discarded along with the surface.
475
+ *
476
+ * The callback is host code, so it is assumed to be able to throw. Two consequences are guarded
477
+ * here: the throw must not escape into the MCP handler's catch (which would reply `ok: false`
478
+ * for a call that actually succeeded), and it must not consume the latch (which would leave the
479
+ * surface with no response AND no further chance of being told to remount).
480
+ */
481
+ private reportVersionSkew;
360
482
  private handleInvokeTool;
361
483
  private handleGetResource;
362
484
  private handleUploadDocument;
363
485
  private replyError;
364
486
  }
365
487
 
366
- export { DEFAULT_MAX_CONCURRENT_MCP_REQUESTS as D, type InputEventPayload as I, type McpHttpClient as M, WORKER_TRANSPORT_PROTOCOL as W, type McpHttpRequest as a, type McpUploadRequest as b, type WorkerCtor as c, type WorkerHandshakePayload as d, type WorkerLike as e, WorkerRemoteDomTransport as f, type WorkerRemoteDomTransportOptions as g };
488
+ export { CAPABILITY_REJECTION_REASON_HEADER as C, DEFAULT_MAX_CONCURRENT_MCP_REQUESTS as D, EXTENSION_ACTIVE_VERSION_HEADER as E, type InputEventPayload as I, type McpHttpClient as M, VERSION_MISMATCH_REASON as V, WORKER_TRANSPORT_PROTOCOL as W, EXTENSION_VERSION_HEADER as a, type McpHttpRequest as b, type McpHttpResponse as c, type McpResponseMetadata as d, type McpUploadRequest as e, type VersionSkew as f, type VersionSkewSource as g, type WorkerCtor as h, type WorkerHandshakePayload as i, type WorkerLike as j, WorkerRemoteDomTransport as k, type WorkerRemoteDomTransportOptions as l, detectVersionSkew as m };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ethisyscore/extension-runtime",
3
- "version": "1.58.0",
3
+ "version": "1.61.0",
4
4
  "description": "Host + plugin runtime for EthisysCore Contract A (host-rendered) and Contract B (worker remote-runtime) extensions.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",