@ethisyscore/extension-runtime 1.59.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,4 +1,4 @@
1
- import { a as BridgePushA11yEnvelope, d as BridgePushThemeEnvelope, c as BridgePushLocaleEnvelope, b as BridgePushDensityEnvelope, B as BridgeNavPushEnvelope, e as BridgeSessionTokenPushEnvelope } from './bridge-envelopes-DA6vxbyb.js';
1
+ import { d as BridgePushThemeEnvelope, c as BridgePushLocaleEnvelope, b as BridgePushDensityEnvelope, a as BridgePushA11yEnvelope, B as BridgeNavPushEnvelope, e as BridgeSessionTokenPushEnvelope } from './bridge-envelopes-DA6vxbyb.js';
2
2
 
3
3
  /**
4
4
  * Plugin-side bridge client for the host ↔ plugin platform contract
@@ -1,4 +1,4 @@
1
- import { a as BridgePushA11yEnvelope, d as BridgePushThemeEnvelope, c as BridgePushLocaleEnvelope, b as BridgePushDensityEnvelope, B as BridgeNavPushEnvelope, e as BridgeSessionTokenPushEnvelope } from './bridge-envelopes-DA6vxbyb.cjs';
1
+ import { d as BridgePushThemeEnvelope, c as BridgePushLocaleEnvelope, b as BridgePushDensityEnvelope, a as BridgePushA11yEnvelope, B as BridgeNavPushEnvelope, e as BridgeSessionTokenPushEnvelope } from './bridge-envelopes-DA6vxbyb.cjs';
2
2
 
3
3
  /**
4
4
  * Plugin-side bridge client for the host ↔ plugin platform contract
@@ -287,6 +287,50 @@ function createInputEventCoalescer(sink, options = {}) {
287
287
  };
288
288
  }
289
289
 
290
+ // src/host/version-skew.ts
291
+ var EXTENSION_VERSION_HEADER = "x-extension-version";
292
+ var EXTENSION_ACTIVE_VERSION_HEADER = "x-extension-active-version";
293
+ var CAPABILITY_REJECTION_REASON_HEADER = "x-capability-token-rejection-reason";
294
+ var VERSION_MISMATCH_REASON = "extensionversionmismatch";
295
+ function readHeader(headers, name) {
296
+ if (!headers) {
297
+ return void 0;
298
+ }
299
+ const direct = headers[name];
300
+ if (direct !== void 0) {
301
+ return direct;
302
+ }
303
+ const lowered = name.toLowerCase();
304
+ for (const key of Object.keys(headers)) {
305
+ if (key.toLowerCase() === lowered) {
306
+ return headers[key];
307
+ }
308
+ }
309
+ return void 0;
310
+ }
311
+ function trimmedOrUndefined(value) {
312
+ const trimmed = value?.trim();
313
+ return trimmed !== void 0 && trimmed.length > 0 ? trimmed : void 0;
314
+ }
315
+ function detectVersionSkew(response, mountedExtensionVersion) {
316
+ const reason = trimmedOrUndefined(readHeader(response.headers, CAPABILITY_REJECTION_REASON_HEADER));
317
+ if (reason !== void 0 && reason.toLowerCase() === VERSION_MISMATCH_REASON) {
318
+ return {
319
+ activeVersion: trimmedOrUndefined(readHeader(response.headers, EXTENSION_ACTIVE_VERSION_HEADER)),
320
+ detectedFrom: "rejection"
321
+ };
322
+ }
323
+ if (!response.ok) {
324
+ return null;
325
+ }
326
+ const mounted = trimmedOrUndefined(mountedExtensionVersion);
327
+ const served = trimmedOrUndefined(readHeader(response.headers, EXTENSION_VERSION_HEADER));
328
+ if (mounted === void 0 || served === void 0 || served === mounted) {
329
+ return null;
330
+ }
331
+ return { activeVersion: served, detectedFrom: "response-stamp" };
332
+ }
333
+
290
334
  // src/host/worker/bridge-envelopes.ts
291
335
  var BRIDGE_PUSH_THEME = "ethisys:bridge:theme";
292
336
  var BRIDGE_PUSH_LOCALE = "ethisys:bridge:locale";
@@ -306,6 +350,9 @@ var WorkerRemoteDomTransport = class {
306
350
  workerPort;
307
351
  mcpClient;
308
352
  capabilityTokenProvider;
353
+ mountedExtensionVersion;
354
+ onVersionSkew;
355
+ versionSkewReported = false;
309
356
  coalesceMs;
310
357
  maxConcurrentMcpRequests;
311
358
  abortController;
@@ -328,6 +375,8 @@ var WorkerRemoteDomTransport = class {
328
375
  }
329
376
  this.mcpClient = options.mcpClient;
330
377
  this.capabilityTokenProvider = options.capabilityToken;
378
+ this.mountedExtensionVersion = options.mountedExtensionVersion;
379
+ this.onVersionSkew = options.onVersionSkew;
331
380
  this.coalesceMs = options.coalesceMs ?? 16;
332
381
  this.maxConcurrentMcpRequests = Math.max(
333
382
  1,
@@ -611,6 +660,34 @@ var WorkerRemoteDomTransport = class {
611
660
  requestSignal(id) {
612
661
  return this.inFlightAborts.get(id)?.signal ?? this.abortController.signal;
613
662
  }
663
+ /**
664
+ * Raises the skew callback the first time this surface is seen to be running against a version
665
+ * that is no longer installed.
666
+ *
667
+ * Latched deliberately. A page typically has several MCP calls in flight, and after an upgrade
668
+ * every one of them reports the same skew — firing per response would ask the host to remount
669
+ * the same surface repeatedly, and the second remount would tear down the first. One signal per
670
+ * transport is all a remount needs, and the transport is discarded along with the surface.
671
+ *
672
+ * The callback is host code, so it is assumed to be able to throw. Two consequences are guarded
673
+ * here: the throw must not escape into the MCP handler's catch (which would reply `ok: false`
674
+ * for a call that actually succeeded), and it must not consume the latch (which would leave the
675
+ * surface with no response AND no further chance of being told to remount).
676
+ */
677
+ reportVersionSkew(response) {
678
+ if (this.versionSkewReported || this.onVersionSkew === void 0) {
679
+ return;
680
+ }
681
+ const skew = detectVersionSkew(response, this.mountedExtensionVersion);
682
+ if (skew === null) {
683
+ return;
684
+ }
685
+ try {
686
+ this.onVersionSkew(skew);
687
+ this.versionSkewReported = true;
688
+ } catch {
689
+ }
690
+ }
614
691
  async handleInvokeTool(message) {
615
692
  let token;
616
693
  try {
@@ -630,6 +707,7 @@ var WorkerRemoteDomTransport = class {
630
707
  capabilityToken: token,
631
708
  signal: this.requestSignal(message.id)
632
709
  });
710
+ this.reportVersionSkew(result);
633
711
  this.safePostMessage({
634
712
  id: message.id,
635
713
  type: "ethisys:mcp:invokeTool:result",
@@ -659,6 +737,7 @@ var WorkerRemoteDomTransport = class {
659
737
  capabilityToken: token,
660
738
  signal: this.requestSignal(message.id)
661
739
  });
740
+ this.reportVersionSkew(result);
662
741
  this.safePostMessage({
663
742
  id: message.id,
664
743
  type: "ethisys:mcp:getResource:result",
@@ -689,6 +768,7 @@ var WorkerRemoteDomTransport = class {
689
768
  capabilityToken: token,
690
769
  signal: this.requestSignal(message.id)
691
770
  });
771
+ this.reportVersionSkew(result);
692
772
  this.safePostMessage({
693
773
  id: message.id,
694
774
  type: "ethisys:mcp:uploadDocument:result",
@@ -731,6 +811,9 @@ var IframeBridgeTransport = class {
731
811
  maxConcurrentMcpRequests;
732
812
  maxMessagesPerSecond;
733
813
  onSecurityEvent;
814
+ mountedExtensionVersion;
815
+ onVersionSkew;
816
+ versionSkewReported = false;
734
817
  abortController = new AbortController();
735
818
  hostPort;
736
819
  framePort;
@@ -745,6 +828,8 @@ var IframeBridgeTransport = class {
745
828
  this.targetOrigin = options.targetOrigin;
746
829
  this.capabilityTokenProvider = options.capabilityToken;
747
830
  this.mcpClient = options.mcpClient;
831
+ this.mountedExtensionVersion = options.mountedExtensionVersion;
832
+ this.onVersionSkew = options.onVersionSkew;
748
833
  this.maxConcurrentMcpRequests = Math.max(1, options.maxConcurrentMcpRequests ?? DEFAULT_MAX_CONCURRENT_MCP_REQUESTS);
749
834
  this.maxMessagesPerSecond = Math.max(1, options.maxMessagesPerSecond ?? 64);
750
835
  this.onSecurityEvent = options.onSecurityEvent;
@@ -897,6 +982,7 @@ var IframeBridgeTransport = class {
897
982
  capabilityToken: token,
898
983
  signal: this.abortController.signal
899
984
  });
985
+ this.reportVersionSkew(result);
900
986
  this.safePostMessage({
901
987
  id: message.id,
902
988
  type: "ethisys:mcp:invokeTool:result",
@@ -908,6 +994,30 @@ var IframeBridgeTransport = class {
908
994
  this.replyError(message.id, "ethisys:mcp:invokeTool:result", err);
909
995
  }
910
996
  }
997
+ /**
998
+ * Raises the skew callback the first time this surface is seen to be running against a version
999
+ * that is no longer installed. Latched: a page usually has several MCP calls in flight and after
1000
+ * an upgrade every one of them reports the same skew, so firing per response would ask the host
1001
+ * to remount the same surface repeatedly and each remount would tear down the last.
1002
+ *
1003
+ * The callback is host code and may throw. That must neither escape into the MCP handler's catch
1004
+ * (replying `ok: false` for a call that succeeded) nor consume the latch (leaving the surface with
1005
+ * no response and no further chance of being told to remount).
1006
+ */
1007
+ reportVersionSkew(response) {
1008
+ if (this.versionSkewReported || this.onVersionSkew === void 0) {
1009
+ return;
1010
+ }
1011
+ const skew = detectVersionSkew(response, this.mountedExtensionVersion);
1012
+ if (skew === null) {
1013
+ return;
1014
+ }
1015
+ try {
1016
+ this.onVersionSkew(skew);
1017
+ this.versionSkewReported = true;
1018
+ } catch {
1019
+ }
1020
+ }
911
1021
  async handleGetResource(message) {
912
1022
  let token;
913
1023
  try {
@@ -926,6 +1036,7 @@ var IframeBridgeTransport = class {
926
1036
  capabilityToken: token,
927
1037
  signal: this.abortController.signal
928
1038
  });
1039
+ this.reportVersionSkew(result);
929
1040
  this.safePostMessage({
930
1041
  id: message.id,
931
1042
  type: "ethisys:mcp:getResource:result",
@@ -965,16 +1076,21 @@ Object.defineProperty(exports, "createRemoteComponentRenderer", {
965
1076
  enumerable: true,
966
1077
  get: function () { return host.createRemoteComponentRenderer; }
967
1078
  });
1079
+ exports.CAPABILITY_REJECTION_REASON_HEADER = CAPABILITY_REJECTION_REASON_HEADER;
968
1080
  exports.CONTRACT_B_PRIMITIVES = CONTRACT_B_PRIMITIVES;
969
1081
  exports.DEFAULT_MAX_CONCURRENT_MCP_REQUESTS = DEFAULT_MAX_CONCURRENT_MCP_REQUESTS;
970
1082
  exports.DEFAULT_OFFSCREEN_COALESCE_MS = DEFAULT_OFFSCREEN_COALESCE_MS;
1083
+ exports.EXTENSION_ACTIVE_VERSION_HEADER = EXTENSION_ACTIVE_VERSION_HEADER;
1084
+ exports.EXTENSION_VERSION_HEADER = EXTENSION_VERSION_HEADER;
971
1085
  exports.IFRAME_BRIDGE_PROTOCOL = IFRAME_BRIDGE_PROTOCOL;
972
1086
  exports.IframeBridgeTransport = IframeBridgeTransport;
973
1087
  exports.SemanticComponentRegistry = SemanticComponentRegistry;
1088
+ exports.VERSION_MISMATCH_REASON = VERSION_MISMATCH_REASON;
974
1089
  exports.WORKER_TRANSPORT_PROTOCOL = WORKER_TRANSPORT_PROTOCOL;
975
1090
  exports.WorkerRemoteDomTransport = WorkerRemoteDomTransport;
976
1091
  exports.createInputEventCoalescer = createInputEventCoalescer;
977
1092
  exports.createOffscreenCanvasTransfer = createOffscreenCanvasTransfer;
1093
+ exports.detectVersionSkew = detectVersionSkew;
978
1094
  exports.evaluate = evaluate;
979
1095
  exports.interpret = interpret;
980
1096
  //# sourceMappingURL=index.cjs.map