@ethisyscore/extension-runtime 1.9.2 → 1.10.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 +1 @@
1
- {"version":3,"sources":["../../src/host/declarative/evaluator.ts","../../src/host/declarative/interpreter.ts","../../src/host/worker/component-registry.ts","../../src/host/worker/offscreen.ts","../../src/host/worker/transport.ts"],"names":[],"mappings":";;;;;;AA6BA,IAAM,kBAAA,GAAqB,IAAI,GAAA,CAAY,eAAe,CAAA;AAiBnD,SAAS,QAAA,CAAS,MAAe,OAAA,EACxC;AAEI,EAAA,IAAI,SAAS,IAAA,EACb;AACI,IAAA,OAAO,IAAA;AAAA,EACX;AAEA,EAAA,MAAM,IAAI,OAAO,IAAA;AACjB,EAAA,IAAI,CAAA,KAAM,QAAA,IAAY,CAAA,KAAM,QAAA,IAAY,MAAM,SAAA,EAC9C;AACI,IAAA,OAAO,IAAA;AAAA,EACX;AAEA,EAAA,IAAI,MAAM,UAAA,EACV;AACI,IAAA,MAAM,IAAI,MAAM,uDAAuD,CAAA;AAAA,EAC3E;AAEA,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA,EACtB;AACI,IAAA,OAAO,KAAK,GAAA,CAAI,CAAC,MAAM,QAAA,CAAS,CAAA,EAAG,OAAO,CAAC,CAAA;AAAA,EAC/C;AAEA,EAAA,IAAI,MAAM,QAAA,EACV;AACI,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,6BAAA,EAAgC,CAAC,CAAA,CAAA,CAAG,CAAA;AAAA,EACxD;AAGA,EAAA,MAAM,GAAA,GAAM,IAAA;AACZ,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,IAAA,CAAK,GAAG,CAAA;AAC5B,EAAA,IAAI,IAAA,CAAK,WAAW,CAAA,EACpB;AACI,IAAA,MAAM,IAAI,KAAA;AAAA,MACN,CAAA,0DAAA,EAA6D,KAAK,MAAM,CAAA,EAAA;AAAA,KAC5E;AAAA,EACJ;AAEA,EAAA,MAAM,EAAA,GAAK,KAAK,CAAC,CAAA;AACjB,EAAA,IAAI,CAAC,kBAAA,CAAmB,GAAA,CAAI,EAAE,CAAA,EAC9B;AACI,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,kBAAA,EAAqB,EAAE,CAAA,CAAA,CAAG,CAAA;AAAA,EAC9C;AAEA,EAAA,MAAM,OAAA,GAAU,IAAI,EAAE,CAAA;AAGtB,EAAA,IAAI,OAAO,KAAA,EACX;AACI,IAAA,MAAM,OAAO,OAAO,OAAA,KAAY,QAAA,GAC1B,OAAA,GACA,MAAM,OAAA,CAAQ,OAAO,CAAA,IAAK,OAAO,QAAQ,CAAC,CAAA,KAAM,QAAA,GAC5C,OAAA,CAAQ,CAAC,CAAA,GACT,EAAA;AACV,IAAA,OAAO,UAAA,CAAW,MAAM,OAAO,CAAA;AAAA,EACnC;AAEA,EAAA,MAAM,gBAA2B,KAAA,CAAM,OAAA,CAAQ,OAAO,CAAA,GAChD,OAAA,CAAQ,IAAI,CAAC,CAAA,KAAM,QAAA,CAAS,CAAA,EAAG,OAAO,CAAC,CAAA,GACvC,CAAC,QAAA,CAAS,OAAA,EAAS,OAAO,CAAC,CAAA;AAEjC,EAAA,QAAQ,EAAA;AACR,IACI,KAAK,IAAA;AAAM,MAAA,OAAO,aAAA,CAAc,CAAC,CAAA,KAAM,aAAA,CAAc,CAAC,CAAA;AAAA,IACtD,KAAK,IAAA;AAAM,MAAA,OAAO,aAAA,CAAc,CAAC,CAAA,KAAM,aAAA,CAAc,CAAC,CAAA;AAAA,IACtD,KAAK,GAAA;AAAM,MAAA,OAAQ,aAAA,CAAc,CAAC,CAAA,GAAiB,aAAA,CAAc,CAAC,CAAA;AAAA,IAClE,KAAK,GAAA;AAAM,MAAA,OAAQ,aAAA,CAAc,CAAC,CAAA,GAAiB,aAAA,CAAc,CAAC,CAAA;AAAA,IAClE,KAAK,IAAA;AAAM,MAAA,OAAQ,aAAA,CAAc,CAAC,CAAA,IAAiB,aAAA,CAAc,CAAC,CAAA;AAAA,IAClE,KAAK,IAAA;AAAM,MAAA,OAAQ,aAAA,CAAc,CAAC,CAAA,IAAiB,aAAA,CAAc,CAAC,CAAA;AAAA,IAClE,KAAK,KAAA;AAAO,MAAA,OAAO,aAAA,CAAc,MAAM,OAAO,CAAA;AAAA,IAC9C,KAAK,IAAA;AAAO,MAAA,OAAO,aAAA,CAAc,KAAK,OAAO,CAAA;AAAA,IAC7C,KAAK,KAAA;AAAO,MAAA,OAAO,CAAC,cAAc,CAAC,CAAA;AAAA,IACnC,KAAK,GAAA;AAAO,MAAA,OAAO,cAAc,MAAA,CAAO,CAAC,KAAa,CAAA,KAAM,GAAA,GAAO,GAAc,CAAC,CAAA;AAAA,IAClF,KAAK,GAAA;AAAO,MAAA,OAAQ,aAAA,CAAc,CAAC,CAAA,GAAgB,aAAA,CAAc,CAAC,CAAA;AAAA,IAClE,KAAK,GAAA;AAAO,MAAA,OAAO,cAAc,MAAA,CAAO,CAAC,KAAa,CAAA,KAAM,GAAA,GAAO,GAAc,CAAC,CAAA;AAAA,IAClF,KAAK,GAAA;AAAO,MAAA,OAAO,WAAW,aAAA,CAAc,CAAC,CAAA,EAAa,aAAA,CAAc,CAAC,CAAW,CAAA;AAAA,IACpF,KAAK,GAAA;AAAO,MAAA,OAAO,WAAW,aAAA,CAAc,CAAC,CAAA,EAAa,aAAA,CAAc,CAAC,CAAW,CAAA;AAAA,IACpF;AAEI,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,kBAAA,EAAqB,EAAE,CAAA,CAAA,CAAG,CAAA;AAAA;AAEtD;AASA,SAAS,UAAA,CAAW,GAAW,CAAA,EAC/B;AACI,EAAA,IAAI,MAAM,CAAA,IAAK,CAAC,MAAA,CAAO,QAAA,CAAS,CAAC,CAAA,EACjC;AACI,IAAA,MAAM,IAAI,KAAA;AAAA,MACN,eAAe,CAAA,KAAM,CAAA,GAAI,MAAA,GAAS,MAAA,CAAO,CAAC,CAAC,CAAA,wCAAA;AAAA,KAC/C;AAAA,EACJ;AACA,EAAA,OAAO,CAAA,GAAI,CAAA;AACf;AAOA,SAAS,UAAA,CAAW,GAAW,CAAA,EAC/B;AACI,EAAA,IAAI,MAAM,CAAA,IAAK,CAAC,MAAA,CAAO,QAAA,CAAS,CAAC,CAAA,EACjC;AACI,IAAA,MAAM,IAAI,KAAA;AAAA,MACN,aAAa,CAAA,KAAM,CAAA,GAAI,MAAA,GAAS,MAAA,CAAO,CAAC,CAAC,CAAA,wCAAA;AAAA,KAC7C;AAAA,EACJ;AACA,EAAA,OAAO,CAAA,GAAI,CAAA;AACf;AAQA,SAAS,UAAA,CAAW,MAAc,OAAA,EAClC;AACI,EAAA,IAAI,SAAS,EAAA,EACb;AACI,IAAA,OAAO,MAAA;AAAA,EACX;AAEA,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AAC5B,EAAA,IAAI,IAAA,GAAgB,OAAA;AACpB,EAAA,KAAA,MAAW,QAAQ,KAAA,EACnB;AACI,IAAA,IAAI,IAAA,KAAS,IAAA,IAAQ,OAAO,IAAA,KAAS,QAAA,EACrC;AACI,MAAA,OAAO,MAAA;AAAA,IACX;AACA,IAAA,IAAA,GAAQ,KAAiC,IAAI,CAAA;AAAA,EACjD;AACA,EAAA,OAAO,IAAA;AACX;AClKO,SAAS,SAAA,CAAU,MAAgB,QAAA,EAC1C;AACI,EAAA,MAAM,SAAA,GAAY,QAAA,CAAS,IAAA,CAAK,IAAI,CAAA;AACpC,EAAA,IAAI,CAAC,SAAA,EACL;AACI,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,wBAAA,EAA2B,OAAO,IAAA,CAAK,IAAI,CAAC,CAAA,CAAE,CAAA;AAAA,EAClE;AAEA,EAAA,MAAM,QAAA,GAAoC,KAAK,QAAA,EAAU,GAAA;AAAA,IACrD,CAAC,KAAA,EAAO,KAAA,KAAU,cAAA,CAAe,KAAA,EAAO,UAAU,KAAK;AAAA,GAC3D;AAEA,EAAA,OAAO,cAAc,SAAA,EAAW,EAAE,OAAO,IAAA,CAAK,KAAA,IAAS,QAAQ,CAAA;AACnE;AAEA,SAAS,cAAA,CAAe,IAAA,EAAgB,QAAA,EAA6B,KAAA,EACrE;AACI,EAAA,MAAM,SAAA,GAAY,QAAA,CAAS,IAAA,CAAK,IAAI,CAAA;AACpC,EAAA,IAAI,CAAC,SAAA,EACL;AACI,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,wBAAA,EAA2B,OAAO,IAAA,CAAK,IAAI,CAAC,CAAA,CAAE,CAAA;AAAA,EAClE;AAEA,EAAA,MAAM,QAAA,GAAoC,KAAK,QAAA,EAAU,GAAA;AAAA,IACrD,CAAC,KAAA,EAAO,UAAA,KAAe,cAAA,CAAe,KAAA,EAAO,UAAU,UAAU;AAAA,GACrE;AAEA,EAAA,OAAO,aAAA,CAAc,WAAW,EAAE,KAAA,EAAO,KAAK,KAAA,EAAO,GAAA,EAAK,KAAA,EAAM,EAAG,QAAQ,CAAA;AAC/E;;;ACnBO,IAAM,qBAAA,GAAwB;AAAA,EACjC,QAAA;AAAA,EACA,WAAA;AAAA,EACA,MAAA;AAAA,EACA,cAAA;AAAA,EACA,YAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAA;AAAA,EACA,eAAA;AAAA,EACA,cAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAKA,MAAA;AAAA,EACA,MAAA;AAAA,EACA,QAAA;AAAA,EACA;AACJ;AAQA,IAAM,mBAAA,GAA2C,IAAI,GAAA,CAAI,qBAAqB,CAAA;AAevE,IAAM,yBAAA,GAAN,MAAM,0BAAA,CACb;AAAA,EACqB,OAAA,uBAAc,GAAA,EAAuC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ/D,QAAA,CAAS,MAA6B,SAAA,EAC7C;AACI,IAAA,IAAI,CAAC,mBAAA,CAAoB,GAAA,CAAI,IAAI,CAAA,EACjC;AACI,MAAA,MAAM,IAAI,KAAA;AAAA,QACN,oDAAoD,IAAI,CAAA,YAAA,EAC5C,qBAAA,CAAsB,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA;AAAA,OAChD;AAAA,IACJ;AACA,IAAA,IAAI,IAAA,CAAK,OAAA,CAAQ,GAAA,CAAI,IAAI,CAAA,EACzB;AACI,MAAA,MAAM,IAAI,KAAA;AAAA,QACN,mCAAmC,IAAI,CAAA,kFAAA;AAAA,OAE3C;AAAA,IACJ;AACA,IAAA,IAAA,CAAK,OAAA,CAAQ,GAAA,CAAI,IAAA,EAAM,SAAS,CAAA;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQO,QAAQ,IAAA,EACf;AACI,IAAA,IAAI,CAAC,mBAAA,CAAoB,GAAA,CAAI,IAAI,CAAA,EACjC;AACI,MAAA,MAAM,IAAI,KAAA;AAAA,QACN,oDAAoD,IAAI,CAAA,YAAA,EAC5C,qBAAA,CAAsB,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA;AAAA,OAChD;AAAA,IACJ;AACA,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,OAAA,CAAQ,GAAA,CAAI,IAAI,CAAA;AACnC,IAAA,IAAI,UAAU,MAAA,EACd;AACI,MAAA,MAAM,aAAa,IAAA,CAAK,cAAA,EAAe,CAAE,IAAA,CAAK,IAAI,CAAA,IAAK,QAAA;AACvD,MAAA,MAAM,IAAI,KAAA;AAAA,QACN,CAAA,gCAAA,EAAmC,IAAI,CAAA,4CAAA,EACb,UAAU,CAAA,CAAA;AAAA,OACxC;AAAA,IACJ;AACA,IAAA,OAAO,KAAA;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,IAAI,IAAA,EACX;AACI,IAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,GAAA,CAAI,IAAI,CAAA;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,cAAA,GACP;AACI,IAAA,OAAO,CAAC,GAAG,IAAA,CAAK,QAAQ,IAAA,EAAM,EAAE,IAAA,EAAK;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,OAAc,QAAW,GAAA,EACzB;AACI,IAAA,MAAM,QAAA,GAAW,IAAI,0BAAA,EAA6B;AAClD,IAAA,MAAM,UAAmC,EAAC;AAC1C,IAAA,KAAA,MAAW,QAAQ,qBAAA,EACnB;AACI,MAAA,MAAM,SAAA,GAAa,IAAkD,IAAI,CAAA;AACzE,MAAA,IAAI,cAAc,MAAA,EAClB;AACI,QAAA,OAAA,CAAQ,KAAK,IAAI,CAAA;AACjB,QAAA;AAAA,MACJ;AACA,MAAA,QAAA,CAAS,QAAA,CAAS,MAAM,SAAS,CAAA;AAAA,IACrC;AACA,IAAA,IAAI,OAAA,CAAQ,SAAS,CAAA,EACrB;AACI,MAAA,MAAM,IAAI,KAAA;AAAA,QACN,CAAA,+DAAA,EAAkE,OAAA,CAAQ,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA;AAAA,OACxF;AAAA,IACJ;AACA,IAAA,OAAO,QAAA;AAAA,EACX;AACJ;;;ACjJO,IAAM,6BAAA,GAAgC;AAyE7C,IAAM,mBAAA,uBAAsD,OAAA,EAA2B;AAahF,SAAS,8BACZ,OAAA,EAEJ;AACI,EAAA,MAAM,EAAE,MAAA,EAAQ,SAAA,EAAW,WAAA,EAAY,GAAI,OAAA;AAC3C,EAAA,MAAM,QAAQ,iBAAA,CAAkB,SAAA;AAGhC,EAAA,IAAI,OAAO,KAAA,CAAM,0BAAA,KAA+B,UAAA,EAChD;AACI,IAAA,MAAM,IAAI,KAAA;AAAA,MACN;AAAA,KAEJ;AAAA,EACJ;AACA,EAAA,IAAI,mBAAA,CAAoB,GAAA,CAAI,MAAM,CAAA,EAClC;AACI,IAAA,MAAM,IAAI,KAAA;AAAA,MACN,6DAA6D,SAAS,CAAA,wEAAA;AAAA,KAE1E;AAAA,EACJ;AAEA,EAAA,MAAM,SAAA,GAAY,OAAO,0BAAA,EAA2B;AACpD,EAAA,mBAAA,CAAoB,IAAI,MAAM,CAAA;AAE9B,EAAA,MAAM,QAAA,GAAqC;AAAA,IACvC,IAAA,EAAM,4BAAA;AAAA,IACN,SAAA;AAAA,IACA,OAAO,MAAA,CAAO,KAAA;AAAA,IACd,QAAQ,MAAA,CAAO,MAAA;AAAA,IACf;AAAA,GACJ;AACA,EAAA,WAAA,CAAY,QAAA,EAAU,CAAC,SAAS,CAAC,CAAA;AAEjC,EAAA,OAAO,EAAE,SAAA,EAAU;AACvB;AAiCO,SAAS,yBAAA,CACZ,IAAA,EACA,OAAA,GAAsC,EAAC,EAE3C;AACI,EAAA,IAAI,OAAA,CAAQ,aAAa,IAAA,EACzB;AAEI,IAAA,OAAO,CAAC,OAAA,KACR;AACI,MAAA,IAAA,CAAK,OAAO,CAAA;AAAA,IAChB,CAAA;AAAA,EACJ;AACA,EAAA,MAAM,UAAA,GAAa,QAAQ,UAAA,IAAc,6BAAA;AACzC,EAAA,IAAI,OAAA;AACJ,EAAA,IAAI,UAAA,GAAa,KAAA;AACjB,EAAA,IAAI,KAAA;AAEJ,EAAA,MAAM,QAAQ,MACd;AACI,IAAA,KAAA,GAAQ,MAAA;AACR,IAAA,IAAI,CAAC,UAAA,EACL;AACI,MAAA;AAAA,IACJ;AACA,IAAA,MAAM,KAAA,GAAQ,OAAA;AACd,IAAA,OAAA,GAAU,MAAA;AACV,IAAA,UAAA,GAAa,KAAA;AACb,IAAA,IAAA,CAAK,KAAK,CAAA;AAAA,EACd,CAAA;AAEA,EAAA,OAAO,CAAC,OAAA,KACR;AACI,IAAA,OAAA,GAAU,OAAA;AACV,IAAA,UAAA,GAAa,IAAA;AACb,IAAA,IAAI,UAAU,MAAA,EACd;AACI,MAAA,KAAA,GAAQ,UAAA,CAAW,OAAO,UAAU,CAAA;AAAA,IACxC;AAAA,EACJ,CAAA;AACJ;;;AC1DO,IAAM,yBAAA,GAA4B;AAmElC,IAAM,mCAAA,GAAsC;AAE5C,IAAM,2BAAN,MACP;AAAA,EACqB,MAAA;AAAA,EACA,QAAA;AAAA,EACA,UAAA;AAAA,EACA,SAAA;AAAA,EACA,uBAAA;AAAA,EACA,UAAA;AAAA,EACA,wBAAA;AAAA,EACA,eAAA;AAAA,EACT,mBAAA,GAAsB,CAAA;AAAA,EACtB,iBAAA;AAAA,EACA,QAAA,GAAW,KAAA;AAAA,EACX,SAAA,GAAY,KAAA;AAAA,EAEb,YAAY,OAAA,EACnB;AACI,IAAA,MAAM,UAAA,GAAa,OAAA,CAAQ,UAAA,IAAe,UAAA,CAAiD,MAAA;AAC3F,IAAA,IAAI,CAAC,UAAA,EACL;AACI,MAAA,MAAM,IAAI,MAAM,2DAA2D,CAAA;AAAA,IAC/E;AAEA,IAAA,IAAA,CAAK,YAAY,OAAA,CAAQ,SAAA;AACzB,IAAA,IAAA,CAAK,0BAA0B,OAAA,CAAQ,eAAA;AACvC,IAAA,IAAA,CAAK,UAAA,GAAa,QAAQ,UAAA,IAAc,EAAA;AACxC,IAAA,IAAA,CAAK,2BAA2B,IAAA,CAAK,GAAA;AAAA,MACjC,CAAA;AAAA,MACA,QAAQ,wBAAA,IAA4B;AAAA,KACxC;AAIA,IAAA,IAAA,CAAK,eAAA,GAAkB,IAAI,eAAA,EAAgB;AAI3C,IAAA,IAAA,CAAK,MAAA,GAAS,IAAI,UAAA,CAAW,OAAA,CAAQ,cAAc,EAAE,IAAA,EAAM,UAAU,CAAA;AAErE,IAAA,MAAM,OAAA,GAAU,IAAI,cAAA,EAAe;AACnC,IAAA,IAAA,CAAK,WAAW,OAAA,CAAQ,KAAA;AACxB,IAAA,IAAA,CAAK,aAAa,OAAA,CAAQ,KAAA;AAK1B,IAAA,IAAA,CAAK,SAAS,SAAA,GAAY,CAAC,OAAO,IAAA,CAAK,iBAAA,CAAkB,GAAG,IAAsB,CAAA;AAClF,IAAA,IAAA,CAAK,SAAS,KAAA,EAAM;AAEpB,IAAA,IAAA,CAAK,MAAA,CAAO,OAAA,GAAU,CAAC,EAAA,KACvB;AAIS,IACT,CAAA;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyBO,OAAA,CAAQ,WAAmB,SAAA,EAClC;AACI,IAAA,IAAI,KAAK,SAAA,EACT;AACI,MAAA;AAAA,IACJ;AACA,IAAA,IAAA,CAAK,SAAA,GAAY,IAAA;AAOjB,IAAA,MAAM,SAAA,GAAoC,OAAO,MAAA,CAAO;AAAA,MACpD,IAAA,EAAM,0BAAA;AAAA,MACN,QAAA,EAAU,yBAAA;AAAA,MACV,SAAA;AAAA,MACA,WAAW,MAAA,CAAO,MAAA,CAAO,EAAE,GAAG,WAAW;AAAA,KAC5C,CAAA;AACD,IAAA,IAAA,CAAK,OAAO,WAAA,CAAY,SAAA,EAAW,CAAC,IAAA,CAAK,UAAU,CAAC,CAAA;AAAA,EACxD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQO,YAAY,QAAA,EACnB;AACI,IAAA,IAAA,CAAK,iBAAA,GAAoB,QAAA;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBO,cAAA,CACH,QACA,OAAA,EAEJ;AACI,IAAA,OAAO,6BAAA,CAA8B;AAAA,MACjC,MAAA;AAAA,MACA,WAAW,OAAA,CAAQ,SAAA;AAAA,MACnB,WAAA,EAAa,CAAC,OAAA,EAAS,QAAA,KACvB;AACI,QAAA,IAAA,CAAK,QAAA,CAAS,WAAA,CAAY,OAAA,EAAS,QAAQ,CAAA;AAAA,MAC/C;AAAA,KACH,CAAA;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaO,eAAe,OAAA,EACtB;AACI,IAAA,IAAA,CAAK,SAAS,WAAA,CAAY;AAAA,MACtB,IAAA,EAAM,qBAAA;AAAA,MACN;AAAA,KACH,CAAA;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASO,gBAAmB,QAAA,EAC1B;AACI,IAAA,IAAI,OAAA;AACJ,IAAA,IAAI,KAAA;AACJ,IAAA,MAAM,QAAQ,MACd;AACI,MAAA,KAAA,GAAQ,MAAA;AACR,MAAA,MAAM,KAAA,GAAQ,OAAA;AACd,MAAA,OAAA,GAAU,MAAA;AACV,MAAA,IAAI,UAAU,MAAA,EACd;AACI,QAAA,QAAA,CAAS,KAAK,CAAA;AAAA,MAClB;AAAA,IACJ,CAAA;AACA,IAAA,OAAO,CAAC,OAAA,KACR;AACI,MAAA,OAAA,GAAU,OAAA;AACV,MAAA,IAAI,UAAU,MAAA,EACd;AACI,QAAA,KAAA,GAAQ,UAAA,CAAW,KAAA,EAAO,IAAA,CAAK,UAAU,CAAA;AAAA,MAC7C;AAAA,IACJ,CAAA;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUO,OAAA,GACP;AACI,IAAA,IAAI,KAAK,QAAA,EACT;AACI,MAAA;AAAA,IACJ;AACA,IAAA,IAAA,CAAK,QAAA,GAAW,IAAA;AAChB,IAAA,IACA;AACI,MAAA,IAAA,CAAK,gBAAgB,KAAA,EAAM;AAAA,IAC/B,CAAA,CAAA,MAEA;AAAA,IAIA;AACA,IAAA,IACA;AACI,MAAA,IAAA,CAAK,SAAS,KAAA,EAAM;AAAA,IACxB,CAAA,CAAA,MAEA;AAAA,IAEA;AACA,IAAA,IACA;AACI,MAAA,IAAA,CAAK,OAAO,SAAA,EAAU;AAAA,IAC1B,CAAA,CAAA,MAEA;AAAA,IAEA;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,gBAAgB,OAAA,EACxB;AACI,IAAA,IAAI,KAAK,QAAA,EACT;AACI,MAAA;AAAA,IACJ;AACA,IAAA,IACA;AACI,MAAA,IAAA,CAAK,QAAA,CAAS,YAAY,OAAO,CAAA;AAAA,IACrC,CAAA,CAAA,MAEA;AAAA,IAIA;AAAA,EACJ;AAAA;AAAA,EAIQ,kBAAkB,OAAA,EAC1B;AAII,IAAA,IAAI,KAAK,QAAA,EACT;AACI,MAAA;AAAA,IACJ;AACA,IAAA,QAAQ,QAAQ,IAAA;AAChB,MACA,KAAK,wBAAA;AACD,QAAA,IAAA,CAAK,WAAA,CAAY,SAA8B,+BAAA,EAAiC,CAAC,MAAM,IAAA,CAAK,gBAAA,CAAiB,CAAC,CAAC,CAAA;AAC/G,QAAA;AAAA,MAEJ,KAAK,yBAAA;AACD,QAAA,IAAA,CAAK,WAAA,CAAY,SAA+B,gCAAA,EAAkC,CAAC,MAAM,IAAA,CAAK,iBAAA,CAAkB,CAAC,CAAC,CAAA;AAClH,QAAA;AAAA,MAEJ,KAAK,mBAAA;AACD,QAAA,IAAA,CAAK,iBAAA,GAAqB,QAA6B,OAAO,CAAA;AAC9D,QAAA;AAAA,MAEJ;AAII,QAAA;AAAA;AACJ,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,WAAA,CACJ,OAAA,EACA,UAAA,EACA,OAAA,EAEJ;AACI,IAAA,IAAI,IAAA,CAAK,mBAAA,IAAuB,IAAA,CAAK,wBAAA,EACrC;AAII,MAAA,IAAA,CAAK,eAAA,CAAgB;AAAA,QACjB,IAAI,OAAA,CAAQ,EAAA;AAAA,QACZ,IAAA,EAAM,UAAA;AAAA,QACN,EAAA,EAAI,KAAA;AAAA,QACJ,KAAA,EAAO,CAAA,oCAAA,EAAuC,IAAA,CAAK,wBAAwB,CAAA,SAAA;AAAA,OAC9E,CAAA;AACD,MAAA;AAAA,IACJ;AACA,IAAA,IAAA,CAAK,mBAAA,EAAA;AACL,IAAA,OAAA,CAAQ,OAAO,CAAA,CAAE,OAAA,CAAQ,MACzB;AACI,MAAA,IAAA,CAAK,sBAAsB,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,sBAAsB,CAAC,CAAA;AAAA,IACvE,CAAC,CAAA;AAAA,EACL;AAAA,EAEA,MAAc,iBAAiB,OAAA,EAC/B;AACI,IAAA,IAAI,KAAA;AACJ,IAAA,IACA;AACI,MAAA,KAAA,GAAQ,MAAM,KAAK,uBAAA,EAAwB;AAAA,IAC/C,SACO,GAAA,EACP;AACI,MAAA,IAAA,CAAK,UAAA,CAAW,OAAA,CAAQ,EAAA,EAAI,+BAAA,EAAiC,GAAG,CAAA;AAChE,MAAA;AAAA,IACJ;AAIA,IAAA,IAAI,KAAK,QAAA,EACT;AACI,MAAA;AAAA,IACJ;AACA,IAAA,IACA;AACI,MAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,SAAA,CAAU,KAAA,CAAM;AAAA,QACtC,IAAA,EAAM,YAAA;AAAA,QACN,MAAM,OAAA,CAAQ,IAAA;AAAA,QACd,MAAM,OAAA,CAAQ,IAAA;AAAA,QACd,eAAA,EAAiB,KAAA;AAAA,QACjB,MAAA,EAAQ,KAAK,eAAA,CAAgB;AAAA,OAChC,CAAA;AAGD,MAAA,IAAA,CAAK,eAAA,CAAgB;AAAA,QACjB,IAAI,OAAA,CAAQ,EAAA;AAAA,QACZ,IAAA,EAAM,+BAAA;AAAA,QACN,IAAI,MAAA,CAAO,EAAA;AAAA,QACX,MAAM,MAAA,CAAO,IAAA;AAAA,QACb,OAAO,MAAA,CAAO;AAAA,OACjB,CAAA;AAAA,IACL,SACO,GAAA,EACP;AACI,MAAA,IAAA,CAAK,UAAA,CAAW,OAAA,CAAQ,EAAA,EAAI,+BAAA,EAAiC,GAAG,CAAA;AAAA,IACpE;AAAA,EACJ;AAAA,EAEA,MAAc,kBAAkB,OAAA,EAChC;AACI,IAAA,IAAI,KAAA;AACJ,IAAA,IACA;AACI,MAAA,KAAA,GAAQ,MAAM,KAAK,uBAAA,EAAwB;AAAA,IAC/C,SACO,GAAA,EACP;AACI,MAAA,IAAA,CAAK,UAAA,CAAW,OAAA,CAAQ,EAAA,EAAI,gCAAA,EAAkC,GAAG,CAAA;AACjE,MAAA;AAAA,IACJ;AACA,IAAA,IAAI,KAAK,QAAA,EACT;AACI,MAAA;AAAA,IACJ;AACA,IAAA,IACA;AACI,MAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,SAAA,CAAU,KAAA,CAAM;AAAA,QACtC,IAAA,EAAM,aAAA;AAAA,QACN,KAAK,OAAA,CAAQ,GAAA;AAAA,QACb,eAAA,EAAiB,KAAA;AAAA,QACjB,MAAA,EAAQ,KAAK,eAAA,CAAgB;AAAA,OAChC,CAAA;AACD,MAAA,IAAA,CAAK,eAAA,CAAgB;AAAA,QACjB,IAAI,OAAA,CAAQ,EAAA;AAAA,QACZ,IAAA,EAAM,gCAAA;AAAA,QACN,IAAI,MAAA,CAAO,EAAA;AAAA,QACX,MAAM,MAAA,CAAO,IAAA;AAAA,QACb,OAAO,MAAA,CAAO;AAAA,OACjB,CAAA;AAAA,IACL,SACO,GAAA,EACP;AACI,MAAA,IAAA,CAAK,UAAA,CAAW,OAAA,CAAQ,EAAA,EAAI,gCAAA,EAAkC,GAAG,CAAA;AAAA,IACrE;AAAA,EACJ;AAAA,EAEQ,UAAA,CAAW,EAAA,EAAY,IAAA,EAAc,GAAA,EAC7C;AAKI,IAAA,MAAM,OAAA,GAAU,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,oBAAA;AACrD,IAAA,IAAA,CAAK,eAAA,CAAgB,EAAE,EAAA,EAAI,IAAA,EAAM,IAAI,KAAA,EAAO,KAAA,EAAO,SAAS,CAAA;AAAA,EAChE;AACJ","file":"index.js","sourcesContent":["/**\n * Pure JsonLogic evaluator for the curated 14-operator subset declared in\n * `@ethisyscore/protocol`'s {@link KNOWN_OPERATORS}.\n *\n * The evaluator implements **only** the closed vocabulary:\n *\n * - Comparison: `==`, `!=`, `>`, `<`, `>=`, `<=`\n * - Boolean: `and`, `or`, `not`\n * - Arithmetic: `+`, `-`, `*`, `/`, `%`\n * - Reference: `var` (dot-path resolution against the supplied context)\n *\n * Design constraints:\n *\n * - **Pure function.** No I/O, no async, no `eval`, no `Function` construction.\n * - **Closed vocabulary.** Operators outside {@link KNOWN_OPERATORS} cause an\n * immediate throw — there is no extension mechanism for plugin authors.\n * - **No function values.** Function-typed inputs are rejected at every depth.\n * This keeps expression trees serialisable and free of host-side side effects.\n * - **Fail-loud.** Malformed expressions (e.g. an operator object with more or\n * fewer than one key) throw rather than silently returning `undefined`.\n */\nimport { KNOWN_OPERATORS } from \"@ethisyscore/protocol\";\n\n/**\n * Resolution context for `var` references. Keys are looked up via dot-path\n * traversal; missing paths resolve to `undefined`.\n */\nexport type EvaluatorContext = Record<string, unknown>;\n\nconst KNOWN_OPERATOR_SET = new Set<string>(KNOWN_OPERATORS);\n\n/**\n * Evaluate a JsonLogic expression in the closed operator subset against the\n * supplied context.\n *\n * @param expr The expression tree. Scalars (`string | number | boolean | null`)\n * evaluate to themselves; arrays map element-wise; operator\n * objects must contain exactly one key from\n * {@link KNOWN_OPERATORS}.\n * @param context The context bag for `var` resolution. Dot-paths traverse\n * nested objects.\n *\n * @throws Error if `expr` contains an unknown operator, a function-typed value,\n * an operator object with a number of keys other than one, or any\n * other unsupported shape (e.g. `undefined`, `Symbol`, `bigint`).\n */\nexport function evaluate(expr: unknown, context: EvaluatorContext): unknown\n{\n // Literal scalars — return as-is.\n if (expr === null)\n {\n return null;\n }\n\n const t = typeof expr;\n if (t === \"string\" || t === \"number\" || t === \"boolean\")\n {\n return expr;\n }\n\n if (t === \"function\")\n {\n throw new Error(\"Function-typed values are not allowed in expressions.\");\n }\n\n if (Array.isArray(expr))\n {\n return expr.map((e) => evaluate(e, context));\n }\n\n if (t !== \"object\")\n {\n throw new Error(`Unsupported expression type: ${t}.`);\n }\n\n // Operator object — must have exactly one key from the closed vocabulary.\n const obj = expr as Record<string, unknown>;\n const keys = Object.keys(obj);\n if (keys.length !== 1)\n {\n throw new Error(\n `Expression object must have exactly one operator key (got ${keys.length}).`,\n );\n }\n\n const op = keys[0];\n if (!KNOWN_OPERATOR_SET.has(op))\n {\n throw new Error(`Unknown operator: ${op}.`);\n }\n\n const rawArgs = obj[op];\n\n // `var` takes a single dot-path argument and does not recurse over its operand.\n if (op === \"var\")\n {\n const path = typeof rawArgs === \"string\"\n ? rawArgs\n : Array.isArray(rawArgs) && typeof rawArgs[0] === \"string\"\n ? rawArgs[0]\n : \"\";\n return resolveVar(path, context);\n }\n\n const evaluatedArgs: unknown[] = Array.isArray(rawArgs)\n ? rawArgs.map((a) => evaluate(a, context))\n : [evaluate(rawArgs, context)];\n\n switch (op)\n {\n case \"==\": return evaluatedArgs[0] === evaluatedArgs[1];\n case \"!=\": return evaluatedArgs[0] !== evaluatedArgs[1];\n case \">\": return (evaluatedArgs[0] as number) > (evaluatedArgs[1] as number);\n case \"<\": return (evaluatedArgs[0] as number) < (evaluatedArgs[1] as number);\n case \">=\": return (evaluatedArgs[0] as number) >= (evaluatedArgs[1] as number);\n case \"<=\": return (evaluatedArgs[0] as number) <= (evaluatedArgs[1] as number);\n case \"and\": return evaluatedArgs.every(Boolean);\n case \"or\": return evaluatedArgs.some(Boolean);\n case \"not\": return !evaluatedArgs[0];\n case \"+\": return evaluatedArgs.reduce((acc: number, n) => acc + (n as number), 0);\n case \"-\": return (evaluatedArgs[0] as number) - (evaluatedArgs[1] as number);\n case \"*\": return evaluatedArgs.reduce((acc: number, n) => acc * (n as number), 1);\n case \"/\": return safeDivide(evaluatedArgs[0] as number, evaluatedArgs[1] as number);\n case \"%\": return safeModulo(evaluatedArgs[0] as number, evaluatedArgs[1] as number);\n default:\n // Unreachable: gated by KNOWN_OPERATOR_SET above. Kept for exhaustiveness.\n throw new Error(`Unknown operator: ${op}.`);\n }\n}\n\n/**\n * Throw on zero or non-finite denominators in `/`. JavaScript silently coerces\n * `1 / 0` to `Infinity`; that value then leaks into host-rendered props and\n * surfaces as weird UI (or React `<input value={Infinity} />` warnings). The\n * closed evaluator's fail-loud contract demands a real error so the host\n * surface mount can fall back to an `ErrorState`.\n */\nfunction safeDivide(a: number, b: number): number\n{\n if (b === 0 || !Number.isFinite(b))\n {\n throw new Error(\n `Division by ${b === 0 ? \"zero\" : String(b)} is not allowed in the closed evaluator.`,\n );\n }\n return a / b;\n}\n\n/**\n * Throw on zero or non-finite denominators in `%`. JavaScript silently coerces\n * `1 % 0` to `NaN`; that value would compare unequal to itself and corrupt\n * downstream comparisons in reactive rules.\n */\nfunction safeModulo(a: number, b: number): number\n{\n if (b === 0 || !Number.isFinite(b))\n {\n throw new Error(\n `Modulo by ${b === 0 ? \"zero\" : String(b)} is not allowed in the closed evaluator.`,\n );\n }\n return a % b;\n}\n\n/**\n * Resolve a dot-separated path against the supplied context, returning the\n * leaf value or `undefined` if any segment is missing or non-traversable.\n *\n * Empty paths resolve to `undefined`.\n */\nfunction resolveVar(path: string, context: EvaluatorContext): unknown\n{\n if (path === \"\")\n {\n return undefined;\n }\n\n const parts = path.split(\".\");\n let curr: unknown = context;\n for (const part of parts)\n {\n if (curr === null || typeof curr !== \"object\")\n {\n return undefined;\n }\n curr = (curr as Record<string, unknown>)[part];\n }\n return curr;\n}\n","import { createElement, type ReactElement, type ReactNode } from \"react\";\nimport type { ComponentRegistry } from \"./registry\";\nimport type { SduiNode } from \"./types\";\n\n/**\n * Walk a parsed SDUI tree and render it as a React element by looking up each\n * node's `type` in the supplied {@link ComponentRegistry}.\n *\n * The interpreter is purely structural:\n *\n * - It owns no UI styling, layout, or data fetching.\n * - It never reads `node.props` — props are forwarded opaquely to the host\n * component, which owns interpretation per primitive.\n * - It does not evaluate `node.bindings` — reactive rules are handled in a\n * separate task (E2.S2). For v1 the interpreter passes through the static\n * tree only.\n *\n * Each child is given a stable React `key` derived from its position so that\n * React's reconciler can identify list items across renders. The key is a\n * sibling-local index; the registry consumer is responsible for opting into a\n * stable identity if it has a domain-meaningful `props.key`.\n *\n * @throws Error when `node.type` is not present in the registry. This is the\n * fail-loud behaviour required by the closed v1 vocabulary — unknown\n * primitives must not silently degrade.\n */\nexport function interpret(node: SduiNode, registry: ComponentRegistry): ReactElement\n{\n const Component = registry[node.type];\n if (!Component)\n {\n throw new Error(`Unknown SDUI primitive: ${String(node.type)}`);\n }\n\n const children: ReactNode[] | undefined = node.children?.map(\n (child, index) => interpretChild(child, registry, index),\n );\n\n return createElement(Component, { props: node.props }, children);\n}\n\nfunction interpretChild(node: SduiNode, registry: ComponentRegistry, index: number): ReactElement\n{\n const Component = registry[node.type];\n if (!Component)\n {\n throw new Error(`Unknown SDUI primitive: ${String(node.type)}`);\n }\n\n const children: ReactNode[] | undefined = node.children?.map(\n (child, childIndex) => interpretChild(child, registry, childIndex),\n );\n\n return createElement(Component, { props: node.props, key: index }, children);\n}\n","/**\n * Semantic component registry for Contract B (worker remote-runtime)\n * extensions.\n *\n * Worker-side plugin code references UI primitives by name — e.g.\n * `<DataTable />`, `<Drawer />`, `<CanvasSurface />`. The host owns rendering:\n * a {@link SemanticComponentRegistry} maps each name to a concrete component\n * type, and the Remote DOM host receiver consults the registry on every\n * worker-emitted mutation. The closed primitive vocabulary {@link CONTRACT_B_PRIMITIVES}\n * is intentionally narrow — adding a primitive is a coordinated host change so\n * plugins cannot extend the contract ad hoc.\n *\n * **Alignment with SDUI:** Where the Contract B name overlaps with the\n * `@ethisyscore/protocol` SDUI primitive vocabulary (`DataTable`, `Form`), the\n * names are identical to avoid synonym drift. SDUI's `Action` is intentionally\n * NOT reused — Contract B's interactive primitive is named `Button` so the\n * worker authoring API stays close to React DOM idioms. The two vocabularies\n * may converge as they evolve, but the host registries (Contract A SDUI vs\n * Contract B Semantic) remain distinct surfaces today.\n *\n * The registry is renderer-agnostic — the generic `TComponent` parameter is\n * whatever component type the host supplies (React `ComponentType` in\n * production; plain objects in tests). This keeps the registry free of a\n * React peer-dep requirement and lets tests assert behaviour without rendering.\n */\n\n// ─── Closed v1 vocabulary ─────────────────────────────────────────────────────\n\n/**\n * The mandatory Contract B (worker remote-runtime) semantic primitive vocabulary.\n *\n * Adding a primitive requires a coordinated host change + protocol bump —\n * plugins cannot extend this set. Each name has a host-supplied concrete\n * component (gogo-ui in production); the registry enforces 1:1 substitution.\n */\nexport const CONTRACT_B_PRIMITIVES = [\n \"Button\",\n \"DataTable\",\n \"Form\",\n \"EntityPicker\",\n \"CommandBar\",\n \"Drawer\",\n \"Modal\",\n \"CanvasSurface\",\n \"WebGLSurface\",\n // Phase 1 additions — mirror the vite-plugin's CONTRACT_B_SEMANTIC_PRIMITIVES\n // list verbatim; drift between the two is caught by the\n // contract-b.test.ts \"import-map-allowlist matches extension-runtime\n // CONTRACT_B_PRIMITIVES\" test.\n \"Card\",\n \"Tabs\",\n \"Select\",\n \"Alert\",\n] as const;\n\n/**\n * Union type of the Contract B semantic primitive names — exported so callers\n * (tests, hosts) get full IntelliSense and compile-time exhaustiveness checks.\n */\nexport type SemanticPrimitiveName = typeof CONTRACT_B_PRIMITIVES[number];\n\nconst KNOWN_PRIMITIVE_SET: ReadonlySet<string> = new Set(CONTRACT_B_PRIMITIVES);\n\n// ─── Registry ─────────────────────────────────────────────────────────────────\n\n/**\n * Map of semantic primitive name → concrete component for Contract B\n * extensions.\n *\n * Construction is open-ended (callers register primitives one at a time) or\n * eager via {@link SemanticComponentRegistry.fromMap} which forces a complete\n * map up front and aborts if any primitive is missing.\n *\n * @typeParam TComponent - The host's concrete component shape. React hosts pass\n * `ComponentType<P>`; tests pass any tagged object.\n */\nexport class SemanticComponentRegistry<TComponent>\n{\n private readonly entries = new Map<SemanticPrimitiveName, TComponent>();\n\n /**\n * Register a primitive → component binding.\n *\n * Throws if `name` is not in {@link CONTRACT_B_PRIMITIVES} (drift prevention)\n * or if `name` is already registered (silent-override prevention).\n */\n public register(name: SemanticPrimitiveName, component: TComponent): void\n {\n if (!KNOWN_PRIMITIVE_SET.has(name))\n {\n throw new Error(\n `[component-registry] unknown semantic primitive '${name}'. ` +\n `Allowed: ${CONTRACT_B_PRIMITIVES.join(\", \")}.`,\n );\n }\n if (this.entries.has(name))\n {\n throw new Error(\n `[component-registry] primitive '${name}' is already registered. ` +\n `Construct a new registry instead of overriding a binding.`,\n );\n }\n this.entries.set(name, component);\n }\n\n /**\n * Resolve a primitive name to its concrete component. Throws if the name is\n * outside the Contract B vocabulary OR if the registry has no binding —\n * silent fallthrough would let typos render blank components, which is\n * worse than a fast failure for plugin authors.\n */\n public resolve(name: SemanticPrimitiveName): TComponent\n {\n if (!KNOWN_PRIMITIVE_SET.has(name))\n {\n throw new Error(\n `[component-registry] unknown semantic primitive '${name}'. ` +\n `Allowed: ${CONTRACT_B_PRIMITIVES.join(\", \")}.`,\n );\n }\n const found = this.entries.get(name);\n if (found === undefined)\n {\n const registered = this.listRegistered().join(\", \") || \"<none>\";\n throw new Error(\n `[component-registry] primitive '${name}' is not registered. ` +\n `Registered primitives: ${registered}.`,\n );\n }\n return found;\n }\n\n /**\n * Cheap presence probe — never throws. Use this when callers want to\n * fall back to a default component instead of erroring.\n */\n public has(name: SemanticPrimitiveName): boolean\n {\n return this.entries.has(name);\n }\n\n /**\n * Sorted list of registered primitive names. Stable output makes registry\n * diagnostics and snapshot tests deterministic.\n */\n public listRegistered(): SemanticPrimitiveName[]\n {\n return [...this.entries.keys()].sort() as SemanticPrimitiveName[];\n }\n\n /**\n * Build a registry from a complete `Record<SemanticPrimitiveName, T>` map.\n *\n * The `Record` type forces TypeScript to refuse incomplete literals at\n * compile time, AND we re-check at runtime so callers who type-erase the\n * record (e.g., via `as any`) still get a fast failure.\n */\n public static fromMap<T>(map: Record<SemanticPrimitiveName, T>): SemanticComponentRegistry<T>\n {\n const registry = new SemanticComponentRegistry<T>();\n const missing: SemanticPrimitiveName[] = [];\n for (const name of CONTRACT_B_PRIMITIVES)\n {\n const component = (map as Partial<Record<SemanticPrimitiveName, T>>)[name];\n if (component === undefined)\n {\n missing.push(name);\n continue;\n }\n registry.register(name, component);\n }\n if (missing.length > 0)\n {\n throw new Error(\n `[component-registry] fromMap is missing semantic primitive(s): ${missing.join(\", \")}.`,\n );\n }\n return registry;\n }\n}\n","/**\n * OffscreenCanvas transfer helpers + pointer/keyboard coalescing for\n * Contract B (worker remote-runtime) extensions.\n *\n * **Architecture.** Hosts that expose a {@link import(\"./component-registry\").SemanticComponentRegistry}'s\n * `CanvasSurface` / `WebGLSurface` primitive create a host-side `<canvas>`,\n * transfer control to the worker via {@link createOffscreenCanvasTransfer}, and\n * the plugin (running inside the worker realm) calls `offscreen.getContext()`\n * to render. The `<canvas>` is owned by the host DOM tree (so layout, theming,\n * and accessibility properties stay on the host side) but every pixel is\n * produced inside the worker — no per-frame `postMessage` cost, no main-thread\n * blocking.\n *\n * **Pointer / keyboard delivery.** Input events still originate on the host\n * `<canvas>`. To avoid spraying the worker port with per-event traffic the\n * host should wrap pointer-move / wheel / scroll callbacks in\n * {@link createInputEventCoalescer}, matching the trailing-edge coalescer\n * already documented in {@link import(\"./transport\").WorkerRemoteDomTransport}\n * (default 16ms ≈ one rAF). Keyboard events MUST use `discrete: true` —\n * coalescing them would drop characters.\n *\n * **Security.** No capability token, no MCP traffic, and no host-only globals\n * cross via the offscreen transfer. The transfer envelope is plain JSON with a\n * single `OffscreenCanvas` handle in the transfer list — the worker side\n * receives an opaque drawing surface and nothing else.\n */\n\n// ─── Public constants ────────────────────────────────────────────────────────\n\n/**\n * Default trailing-edge coalesce window for pointer / wheel / scroll events,\n * in milliseconds. 16ms ≈ one animation frame at 60Hz. Matches the default\n * documented in {@link import(\"./transport\").WorkerRemoteDomTransport}.\n */\nexport const DEFAULT_OFFSCREEN_COALESCE_MS = 16;\n\n// ─── Transfer envelope ───────────────────────────────────────────────────────\n\n/**\n * Host → worker envelope shape for an `OffscreenCanvas` transfer. Stable wire\n * contract — the worker side decodes by `type` and uses `surfaceId` to bind\n * the offscreen to the right host slot when a plugin renders multiple\n * canvases concurrently.\n */\nexport interface OffscreenTransferMessage\n{\n type: \"ethisys:offscreen:transfer\";\n surfaceId: string;\n width: number;\n height: number;\n offscreen: OffscreenCanvas;\n}\n\n/**\n * Construction options for {@link createOffscreenCanvasTransfer}.\n */\nexport interface OffscreenCanvasTransferOptions\n{\n /**\n * The host-owned `<canvas>` element. Its `width` / `height` are captured\n * before transfer so the worker sees the intended pixel dimensions even if\n * the host element resizes later (host should send a separate resize\n * message in that case).\n */\n canvas: HTMLCanvasElement;\n\n /**\n * Stable identifier the worker uses to route the offscreen to the matching\n * surface slot. Typically the SDUI node id of the `CanvasSurface` /\n * `WebGLSurface` primitive that the worker will draw into.\n */\n surfaceId: string;\n\n /**\n * Host-side postMessage function — typically the\n * {@link import(\"./transport\").WorkerRemoteDomTransport}'s port-side\n * `postMessage`. Tests inject a spy.\n */\n postMessage(message: OffscreenTransferMessage, transfer: Transferable[]): void;\n}\n\n/**\n * Successful transfer result. The caller retains a reference to the\n * `OffscreenCanvas` only for observability (e.g., to wire to the worker's\n * reply handshake) — direct rendering from the host side is forbidden because\n * control has already been transferred and any host-side draw call would\n * throw.\n */\nexport interface OffscreenCanvasTransferResult\n{\n offscreen: OffscreenCanvas;\n}\n\n/**\n * Module-scoped WeakSet tracking canvases this SDK has already transferred.\n *\n * Stored OUTSIDE the DOM node deliberately:\n *\n * - Writing an SDK expando (e.g. `__ethisysOffscreenTransferred`) onto a host-\n * owned `<canvas>` mutates state the host doesn't own. A WeakSet keeps the\n * bookkeeping on the SDK side and lets the canvas object stay pristine.\n * - WeakSet entries are collected when the canvas is GC'd, so the guard does\n * not leak memory for plugins that mount and unmount many surfaces.\n * - WeakSet membership is identity-based, so React/concurrent remount paths\n * that re-use the SAME node still see \"already transferred\"; a fresh DOM\n * node (always allocated by React when the key changes) is a fresh entry.\n */\nconst transferredCanvases: WeakSet<HTMLCanvasElement> = new WeakSet<HTMLCanvasElement>();\n\n/**\n * Transfer control of a host-owned `<canvas>` to the worker.\n *\n * Throws if:\n * - the environment lacks `HTMLCanvasElement.prototype.transferControlToOffscreen`\n * (the helper does NOT silently fall back — Contract B clients must require\n * the API and surface a clear error in unsupported browsers);\n * - the same canvas has already had control transferred (idempotency is the\n * caller's responsibility — re-transferring would throw in the browser\n * anyway, but we raise a clearer, diagnosable message ahead of that).\n */\nexport function createOffscreenCanvasTransfer(\n options: OffscreenCanvasTransferOptions,\n): OffscreenCanvasTransferResult\n{\n const { canvas, surfaceId, postMessage } = options;\n const proto = HTMLCanvasElement.prototype as unknown as {\n transferControlToOffscreen?: () => OffscreenCanvas;\n };\n if (typeof proto.transferControlToOffscreen !== \"function\")\n {\n throw new Error(\n \"[offscreen-canvas] OffscreenCanvas is not supported in this environment. \" +\n \"Contract B canvas surfaces require a browser with HTMLCanvasElement.transferControlToOffscreen.\",\n );\n }\n if (transferredCanvases.has(canvas))\n {\n throw new Error(\n `[offscreen-canvas] canvas already transferred (surfaceId='${surfaceId}'). ` +\n \"Create a fresh <canvas> for each surface instead of re-transferring.\",\n );\n }\n\n const offscreen = canvas.transferControlToOffscreen();\n transferredCanvases.add(canvas);\n\n const envelope: OffscreenTransferMessage = {\n type: \"ethisys:offscreen:transfer\",\n surfaceId,\n width: canvas.width,\n height: canvas.height,\n offscreen,\n };\n postMessage(envelope, [offscreen]);\n\n return { offscreen };\n}\n\n// ─── Coalescing ──────────────────────────────────────────────────────────────\n\n/**\n * Construction options for {@link createInputEventCoalescer}.\n */\nexport interface InputEventCoalescerOptions\n{\n /**\n * Coalesce window in milliseconds. Ignored when {@link discrete} is true.\n * Defaults to {@link DEFAULT_OFFSCREEN_COALESCE_MS} (16ms — one rAF).\n */\n coalesceMs?: number;\n\n /**\n * When `true`, forward every payload synchronously without coalescing.\n * Use this for keyboard events — coalescing would drop characters by\n * collapsing multiple key strokes into one trailing delivery.\n */\n discrete?: boolean;\n}\n\n/**\n * Build a trailing-edge input-event coalescer. The pattern matches\n * {@link import(\"./transport\").WorkerRemoteDomTransport.createCoalescer} —\n * factored out here so OffscreenCanvas callers don't need to construct a full\n * transport just to coalesce pointer-move events.\n *\n * Trailing-edge semantics: every burst within a coalesce window collapses to\n * a single delivery carrying the **last** payload seen in the window. A new\n * payload arriving after a window has flushed starts a fresh window.\n */\nexport function createInputEventCoalescer<T>(\n sink: (payload: T) => void,\n options: InputEventCoalescerOptions = {},\n): (payload: T) => void\n{\n if (options.discrete === true)\n {\n // Discrete mode: pass through unchanged. Keyboard input belongs here.\n return (payload: T) =>\n {\n sink(payload);\n };\n }\n const coalesceMs = options.coalesceMs ?? DEFAULT_OFFSCREEN_COALESCE_MS;\n let pending: T | undefined;\n let pendingSet = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const flush = (): void =>\n {\n timer = undefined;\n if (!pendingSet)\n {\n return;\n }\n const value = pending as T;\n pending = undefined;\n pendingSet = false;\n sink(value);\n };\n\n return (payload: T) =>\n {\n pending = payload;\n pendingSet = true;\n if (timer === undefined)\n {\n timer = setTimeout(flush, coalesceMs);\n }\n };\n}\n","/**\n * Worker-side host transport for `renderMode: remote-runtime` extensions\n * (Contract B). Owns the lifecycle of:\n *\n * 1. A path-pinned module `Worker` spawned from a host-issued bootstrap URL.\n * 2. A `MessagePort`-based RPC channel (handed to the worker on spawn).\n * 3. A Shopify Remote DOM root receiver — wired here so the worker's UI\n * tree mounts into a host React tree.\n * 4. Event coalescing (default ≤16ms, one rAF) for high-frequency input\n * events before they cross the port.\n * 5. MCP tool/resource calls forwarded through the port — but **the\n * capability token never crosses the boundary**. The token is bound\n * to the worker scope on the HOST side: the host transport intercepts\n * the plugin's MCP request, attaches the token to the outbound HTTP\n * call, and forwards ONLY the response payload back through the port.\n *\n * This module is intentionally framework-thin: callers wire `mount(root)`\n * onto a React tree (typically inside a `<WorkerSurfaceMount>`), and the\n * Remote DOM receiver is responsible for translating worker-side\n * RemoteRoot mutations into host-side component renders.\n */\n\nimport { createOffscreenCanvasTransfer } from \"./offscreen\";\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\n/**\n * Structural subset of the DOM `Worker` interface that the transport needs.\n * Tests inject a fake; production callers pass the global `Worker`.\n */\nexport interface WorkerLike\n{\n onmessage: ((ev: MessageEvent) => void) | null;\n onerror: ((ev: ErrorEvent) => void) | null;\n onmessageerror: ((ev: MessageEvent) => void) | null;\n postMessage(message: unknown, transfer?: Transferable[]): void;\n terminate(): void;\n}\n\n/**\n * Constructor signature compatible with the DOM `Worker` constructor. The\n * `workerCtor` option exists exclusively so tests can swap in a fake — in\n * production the caller passes the global `Worker`.\n */\nexport type WorkerCtor = new (url: string | URL, options?: WorkerOptions) => WorkerLike;\n\n/**\n * Discriminated union of MCP fetch shapes the host transport issues. The\n * `capabilityToken` is attached on the host side ONLY — it is never present\n * in any message that crosses the worker port.\n */\nexport type McpHttpRequest =\n | {\n kind: \"invokeTool\";\n name: string;\n args: unknown;\n capabilityToken: string;\n signal?: AbortSignal;\n }\n | {\n kind: \"getResource\";\n uri: string;\n capabilityToken: string;\n signal?: AbortSignal;\n };\n\n/**\n * Host-side HTTP client for MCP calls. Implementations are responsible for\n * carrying the supplied `capabilityToken` on the outbound request (typically\n * via `Authorization: Bearer`).\n */\nexport interface McpHttpClient\n{\n fetch(request: McpHttpRequest): Promise<{ ok: boolean; data?: unknown; error?: string }>;\n}\n\n/**\n * Construction options for {@link WorkerRemoteDomTransport}.\n */\nexport interface WorkerRemoteDomTransportOptions\n{\n /**\n * Canonical host-origin URL for the worker bootstrap script. This MUST\n * be supplied by the host — never built from user input or extension\n * manifest values — so the worker spawn is path-pinned.\n */\n bootstrapUrl: string;\n\n /**\n * Mints (or fetches a cached) capability token. The host transport\n * resolves this for each outbound MCP HTTP call. The token NEVER leaves\n * host scope — plugin code (the worker side) cannot read it.\n */\n capabilityToken: () => Promise<string>;\n\n /**\n * Host-side MCP HTTP client. The transport bridges port-side plugin\n * MCP requests to this client and returns only the response payload\n * back through the port.\n */\n mcpClient: McpHttpClient;\n\n /**\n * Injectable Worker constructor (tests use a fake). Defaults to the\n * global `Worker`.\n */\n workerCtor?: WorkerCtor;\n\n /**\n * Coalescing window in milliseconds for high-frequency events. Defaults\n * to 16ms (~one animation frame). Trailing-edge: the most recent payload\n * within the window is delivered after the window elapses.\n */\n coalesceMs?: number;\n\n /**\n * Maximum number of MCP requests (combined `invokeTool` + `getResource`)\n * the transport will forward to {@link mcpClient} concurrently. Defaults\n * to 8. Requests above the cap are rejected with a deterministic error\n * reply over the port — the worker MUST handle that response shape.\n *\n * Bounded concurrency is a back-pressure boundary, not a quota: it stops\n * a compromised or buggy worker from saturating the host's HTTP / token\n * pool. Production hosts may want to lower this in multi-tenant pools\n * where many extensions share one host process.\n */\n maxConcurrentMcpRequests?: number;\n}\n\n/**\n * Wire shape of a host-sourced input event forwarded to the worker over the\n * port. Pointer / wheel / keyboard payloads share a single envelope type so\n * the worker has one inbound dispatch site. The transport is structurally\n * agnostic to the payload — it forwards verbatim.\n *\n * `kind` discriminates the event family. Coordinates are CSS pixels relative\n * to the host canvas; coalescing happens host-side before the call to\n * {@link WorkerRemoteDomTransport.postInputEvent}.\n */\nexport type InputEventPayload =\n | {\n kind: \"pointermove\" | \"pointerdown\" | \"pointerup\" | \"pointercancel\";\n surfaceId: string;\n x: number;\n y: number;\n buttons: number;\n pointerType: string;\n }\n | {\n kind: \"wheel\";\n surfaceId: string;\n deltaX: number;\n deltaY: number;\n deltaMode: number;\n }\n | {\n kind: \"keydown\" | \"keyup\";\n surfaceId: string;\n key: string;\n code: string;\n ctrlKey: boolean;\n shiftKey: boolean;\n altKey: boolean;\n metaKey: boolean;\n };\n\n/**\n * Documented wire protocol identifier embedded in handshake / message\n * envelopes. Bumped when the worker ↔ host message shape changes in a\n * backward-incompatible way.\n */\nexport const WORKER_TRANSPORT_PROTOCOL = \"ethisys.worker.remotedom.v1\";\n\n/**\n * Wire shape of the handshake message the host posts to the worker on\n * {@link WorkerRemoteDomTransport.connect}. Locked here so the SDK side and the\n * API-hosted bootstrap script (`WorkerBootstrapScriptProvider`) read from the\n * same field names — see the API tests pinning `event.data.moduleUrl` and\n * `event.data.importMap`.\n *\n * The handshake is the SOLE message that ever carries `moduleUrl` or\n * `importMap`; subsequent port traffic uses the discriminated message types\n * declared below. The capability token NEVER appears in this payload (or any\n * other postMessage payload) — it is bound on the host side via\n * {@link WorkerRemoteDomTransportOptions.capabilityToken}.\n */\nexport interface WorkerHandshakePayload\n{\n readonly type: \"ethisys:worker:handshake\";\n readonly protocol: string;\n /** Host-origin URL of the plugin's worker bundle entry. */\n readonly moduleUrl: string;\n /**\n * Frozen bare-specifier → host-origin URL map that the bootstrap installs\n * before importing {@link moduleUrl}. Pulled from the plugin's\n * `worker-bundle.import-map.json` at runtime by the host mount.\n */\n readonly importMap: Readonly<Record<string, string>>;\n}\n\n// ─── Implementation ───────────────────────────────────────────────────────────\n\ninterface InvokeToolMessage\n{\n id: string;\n type: \"ethisys:mcp:invokeTool\";\n name: string;\n args: unknown;\n}\n\ninterface GetResourceMessage\n{\n id: string;\n type: \"ethisys:mcp:getResource\";\n uri: string;\n}\n\ninterface RemoteDomMessage\n{\n type: \"ethisys:remotedom\";\n payload: unknown;\n}\n\ntype InboundMessage = InvokeToolMessage | GetResourceMessage | RemoteDomMessage | { type: string; [k: string]: unknown };\n\n/**\n * Worker-side host transport for Contract B extensions.\n *\n * Construction immediately spawns the worker and transfers one end of a\n * fresh `MessageChannel`; the host retains `port1`, the worker receives\n * `port2`. Both sides exchange messages via their port and the worker port\n * is the *only* channel for plugin ↔ host traffic after handshake.\n */\n/**\n * Default cap for in-flight MCP requests forwarded by the transport. Chosen to\n * cover typical interactive UI traffic (dashboards, list views, detail panels)\n * without letting a runaway worker saturate the host's HTTP / token pool.\n */\nexport const DEFAULT_MAX_CONCURRENT_MCP_REQUESTS = 8;\n\nexport class WorkerRemoteDomTransport\n{\n private readonly worker: WorkerLike;\n private readonly hostPort: MessagePort;\n private readonly workerPort: MessagePort;\n private readonly mcpClient: McpHttpClient;\n private readonly capabilityTokenProvider: () => Promise<string>;\n private readonly coalesceMs: number;\n private readonly maxConcurrentMcpRequests: number;\n private readonly abortController: AbortController;\n private inFlightMcpRequests = 0;\n private remoteDomConsumer: ((payload: unknown) => void) | undefined;\n private disposed = false;\n private connected = false;\n\n public constructor(options: WorkerRemoteDomTransportOptions)\n {\n const WorkerCtor = options.workerCtor ?? (globalThis as unknown as { Worker: WorkerCtor }).Worker;\n if (!WorkerCtor)\n {\n throw new Error(\"WorkerRemoteDomTransport: no Worker constructor available\");\n }\n\n this.mcpClient = options.mcpClient;\n this.capabilityTokenProvider = options.capabilityToken;\n this.coalesceMs = options.coalesceMs ?? 16;\n this.maxConcurrentMcpRequests = Math.max(\n 1,\n options.maxConcurrentMcpRequests ?? DEFAULT_MAX_CONCURRENT_MCP_REQUESTS,\n );\n // Aborted from dispose() — propagates into the mcpClient.fetch path so\n // already-in-flight HTTP calls can short-circuit instead of resolving\n // into a closed port.\n this.abortController = new AbortController();\n\n // Spawn the worker FIRST so the constructor's spawn shape is\n // observable to tests even if MessageChannel construction throws.\n this.worker = new WorkerCtor(options.bootstrapUrl, { type: \"module\" });\n\n const channel = new MessageChannel();\n this.hostPort = channel.port1;\n this.workerPort = channel.port2;\n\n // Wire host-side port handling before {@link connect} posts the\n // handshake — this closes a race where the worker could reply faster\n // than we wire up.\n this.hostPort.onmessage = (ev) => this.handlePortMessage(ev.data as InboundMessage);\n this.hostPort.start();\n\n this.worker.onerror = (ev) =>\n {\n // Defensive: do NOT echo worker errors back to the worker — they\n // may carry plugin-side details. Host-side observability is the\n // platform's responsibility.\n void ev;\n };\n }\n\n /**\n * Post the handshake to the worker. Idempotent — only the FIRST call\n * transfers the {@link MessagePort} and posts the handshake payload;\n * subsequent calls are silent no-ops. Splitting this off from the\n * constructor lets the host mount fetch the per-plugin\n * `worker-bundle.import-map.json` (and resolve the bundle's module URL)\n * before the bootstrap script consumes them.\n *\n * Wire shape: {@link WorkerHandshakePayload}. The capability token NEVER\n * appears in the payload — it's bound on the host side via the\n * {@link WorkerRemoteDomTransportOptions.capabilityToken} provider.\n *\n * The `moduleUrl` and `importMap` are forwarded to the worker so the\n * bootstrap script (served from the host-pinned\n * `/extensions/runtime/worker-bootstrap.js`) can:\n * 1. Compose the frozen, same-origin-validated `IMPORT_MAP` from the\n * handshake payload (rejecting any cross-origin entries).\n * 2. `safeImport(moduleUrl)` the plugin's entry module.\n *\n * Same-origin enforcement is the bootstrap's job — this transport is\n * structurally agnostic to the host origin and only forwards what it's\n * handed.\n */\n public connect(moduleUrl: string, importMap: Record<string, string>): void\n {\n if (this.connected)\n {\n return;\n }\n this.connected = true;\n\n // Snapshot the import map into a fresh object so the worker handshake\n // is decoupled from caller-side mutation. `Object.freeze` here is\n // defence-in-depth — the worker rebuilds with `Object.create(null)`\n // anyway, but freezing the host-side payload makes mutation a typed\n // error for any host code that hangs onto the reference.\n const handshake: WorkerHandshakePayload = Object.freeze({\n type: \"ethisys:worker:handshake\",\n protocol: WORKER_TRANSPORT_PROTOCOL,\n moduleUrl,\n importMap: Object.freeze({ ...importMap }),\n });\n this.worker.postMessage(handshake, [this.workerPort]);\n }\n\n /**\n * Bind a consumer for Remote DOM mutation payloads emitted by the worker.\n * The Remote DOM receiver wiring is owned by the caller (typically a host\n * React component); this method exposes the raw stream so the receiver\n * can integrate cleanly without a circular package import.\n */\n public onRemoteDom(consumer: (payload: unknown) => void): void\n {\n this.remoteDomConsumer = consumer;\n }\n\n /**\n * Transfer control of a host-owned `<canvas>` to the worker so plugin code\n * can render via `OffscreenCanvas`. The host retains the `<canvas>` for\n * layout / accessibility purposes only — every pixel is produced inside the\n * worker, so the main thread is never blocked per frame.\n *\n * The transfer rides the established MessagePort (not the worker global\n * `postMessage`) so it is multiplexed with the rest of the host ↔ worker\n * traffic on the same channel. The `OffscreenCanvas` handle is the sole\n * `Transferable` in the envelope; no capability token, MCP context, or\n * other host-only data crosses the boundary.\n *\n * Throws if the environment lacks `transferControlToOffscreen()` or if the\n * canvas has already been transferred — see {@link createOffscreenCanvasTransfer}\n * for the exact diagnostics.\n */\n public transferCanvas(\n canvas: HTMLCanvasElement,\n options: { surfaceId: string },\n ): { offscreen: OffscreenCanvas }\n {\n return createOffscreenCanvasTransfer({\n canvas,\n surfaceId: options.surfaceId,\n postMessage: (message, transfer) =>\n {\n this.hostPort.postMessage(message, transfer);\n },\n });\n }\n\n /**\n * Forward a host-sourced input event to the worker over the port. The\n * payload shape is opaque to the transport — pointer / wheel / keyboard\n * envelopes share the same wire type. Callers are expected to wrap\n * high-frequency (pointer-move, wheel, scroll) events in\n * {@link createCoalescer} or {@link createInputEventCoalescer} BEFORE\n * calling this method so the port never sees the un-coalesced flood.\n *\n * Keyboard / pointer-down / pointer-up events are discrete and should be\n * delivered without coalescing — pass them straight through.\n */\n public postInputEvent(payload: InputEventPayload): void\n {\n this.hostPort.postMessage({\n type: \"ethisys:input:event\",\n payload,\n });\n }\n\n /**\n * Build a trailing-edge coalescer keyed to {@link WorkerRemoteDomTransportOptions.coalesceMs}.\n *\n * Use it to wrap pointer-move / scroll / resize callbacks **before**\n * they cross the port. The last payload in any coalescing window is the\n * one that wins.\n */\n public createCoalescer<T>(consumer: (payload: T) => void): (payload: T) => void\n {\n let pending: T | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n const flush = () =>\n {\n timer = undefined;\n const value = pending;\n pending = undefined;\n if (value !== undefined)\n {\n consumer(value);\n }\n };\n return (payload: T) =>\n {\n pending = payload;\n if (timer === undefined)\n {\n timer = setTimeout(flush, this.coalesceMs);\n }\n };\n }\n\n /**\n * Tear down the worker and port. Idempotent.\n *\n * Order matters: we abort BEFORE closing the port so any in-flight\n * `mcpClient.fetch` that observes the signal short-circuits and the\n * subsequent attempt to post a reply lands in the disposed-guard branch\n * (which silently drops the post) instead of throwing on a closed port.\n */\n public dispose(): void\n {\n if (this.disposed)\n {\n return;\n }\n this.disposed = true;\n try\n {\n this.abortController.abort();\n }\n catch\n {\n // AbortController.abort() shouldn't throw, but never let dispose\n // crash the caller — teardown must be best-effort across all\n // resources.\n }\n try\n {\n this.hostPort.close();\n }\n catch\n {\n // ignore — port may already be closed\n }\n try\n {\n this.worker.terminate();\n }\n catch\n {\n // ignore\n }\n }\n\n /**\n * Best-effort wrapper around `hostPort.postMessage` that tolerates posts\n * arriving after {@link dispose}. The browser throws on a closed port and\n * the async handlers below can race dispose, so any post initiated by an\n * awaited continuation must be guarded.\n */\n private safePostMessage(message: unknown): void\n {\n if (this.disposed)\n {\n return;\n }\n try\n {\n this.hostPort.postMessage(message);\n }\n catch\n {\n // Port may have been closed between the disposed check and the\n // post call (browser closes ports asynchronously). Swallowing the\n // throw here is safe because we are already on a teardown path.\n }\n }\n\n // ─── Port message handling ──────────────────────────────────────────────\n\n private handlePortMessage(message: InboundMessage): void\n {\n // Post-dispose port traffic is dropped silently. The browser closes\n // MessagePorts asynchronously; any handler invocation after dispose\n // is a race we observe-and-skip rather than try to service.\n if (this.disposed)\n {\n return;\n }\n switch (message.type)\n {\n case \"ethisys:mcp:invokeTool\":\n this.dispatchMcp(message as InvokeToolMessage, \"ethisys:mcp:invokeTool:result\", (m) => this.handleInvokeTool(m));\n return;\n\n case \"ethisys:mcp:getResource\":\n this.dispatchMcp(message as GetResourceMessage, \"ethisys:mcp:getResource:result\", (m) => this.handleGetResource(m));\n return;\n\n case \"ethisys:remotedom\":\n this.remoteDomConsumer?.((message as RemoteDomMessage).payload);\n return;\n\n default:\n // Unknown messages are ignored. The host MUST NOT echo unknown\n // shapes back to the worker — that would create an attacker-\n // controlled reflection surface.\n return;\n }\n }\n\n /**\n * Gate inbound MCP requests through the bounded concurrency window,\n * reject with a deterministic error reply when the cap is exceeded, and\n * decrement the counter unconditionally when the underlying handler\n * settles (regardless of resolution/rejection shape).\n */\n private dispatchMcp<T extends { id: string }>(\n message: T,\n resultType: string,\n handler: (m: T) => Promise<void>,\n ): void\n {\n if (this.inFlightMcpRequests >= this.maxConcurrentMcpRequests)\n {\n // Back-pressure boundary. The worker MUST surface this to plugin\n // code so it can retry / degrade gracefully rather than silently\n // hang on a never-resolving promise.\n this.safePostMessage({\n id: message.id,\n type: resultType,\n ok: false,\n error: `MCP back-pressure: in-flight cap of ${this.maxConcurrentMcpRequests} reached.`,\n });\n return;\n }\n this.inFlightMcpRequests++;\n handler(message).finally(() =>\n {\n this.inFlightMcpRequests = Math.max(0, this.inFlightMcpRequests - 1);\n });\n }\n\n private async handleInvokeTool(message: InvokeToolMessage): Promise<void>\n {\n let token: string;\n try\n {\n token = await this.capabilityTokenProvider();\n }\n catch (err)\n {\n this.replyError(message.id, \"ethisys:mcp:invokeTool:result\", err);\n return;\n }\n // Disposed-before-token: short-circuit. The token mint may have\n // resolved seconds after dispose; posting the reply now would race\n // a closed port.\n if (this.disposed)\n {\n return;\n }\n try\n {\n const result = await this.mcpClient.fetch({\n kind: \"invokeTool\",\n name: message.name,\n args: message.args,\n capabilityToken: token,\n signal: this.abortController.signal,\n });\n // CRITICAL: response payload must never include the token. We\n // forward ONLY the public-facing fields the plugin needs.\n this.safePostMessage({\n id: message.id,\n type: \"ethisys:mcp:invokeTool:result\",\n ok: result.ok,\n data: result.data,\n error: result.error,\n });\n }\n catch (err)\n {\n this.replyError(message.id, \"ethisys:mcp:invokeTool:result\", err);\n }\n }\n\n private async handleGetResource(message: GetResourceMessage): Promise<void>\n {\n let token: string;\n try\n {\n token = await this.capabilityTokenProvider();\n }\n catch (err)\n {\n this.replyError(message.id, \"ethisys:mcp:getResource:result\", err);\n return;\n }\n if (this.disposed)\n {\n return;\n }\n try\n {\n const result = await this.mcpClient.fetch({\n kind: \"getResource\",\n uri: message.uri,\n capabilityToken: token,\n signal: this.abortController.signal,\n });\n this.safePostMessage({\n id: message.id,\n type: \"ethisys:mcp:getResource:result\",\n ok: result.ok,\n data: result.data,\n error: result.error,\n });\n }\n catch (err)\n {\n this.replyError(message.id, \"ethisys:mcp:getResource:result\", err);\n }\n }\n\n private replyError(id: string, type: string, err: unknown): void\n {\n // Surface a sanitised error string — never the raw error object\n // (which may carry stack traces or token fragments from a misbehaving\n // mcpClient implementation). Routed through safePostMessage so a\n // post-dispose reply does not crash on a closed port.\n const message = err instanceof Error ? err.message : \"MCP request failed\";\n this.safePostMessage({ id, type, ok: false, error: message });\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/host/declarative/evaluator.ts","../../src/host/declarative/interpreter.ts","../../src/host/worker/component-registry.ts","../../src/host/worker/offscreen.ts","../../src/host/worker/bridge-envelopes.ts","../../src/host/worker/transport.ts","../../src/host/iframe/transport.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Pure JsonLogic evaluator for the curated 14-operator subset declared in\n * `@ethisyscore/protocol`'s {@link KNOWN_OPERATORS}.\n *\n * The evaluator implements **only** the closed vocabulary:\n *\n * - Comparison: `==`, `!=`, `>`, `<`, `>=`, `<=`\n * - Boolean: `and`, `or`, `not`\n * - Arithmetic: `+`, `-`, `*`, `/`, `%`\n * - Reference: `var` (dot-path resolution against the supplied context)\n *\n * Design constraints:\n *\n * - **Pure function.** No I/O, no async, no `eval`, no `Function` construction.\n * - **Closed vocabulary.** Operators outside {@link KNOWN_OPERATORS} cause an\n * immediate throw — there is no extension mechanism for plugin authors.\n * - **No function values.** Function-typed inputs are rejected at every depth.\n * This keeps expression trees serialisable and free of host-side side effects.\n * - **Fail-loud.** Malformed expressions (e.g. an operator object with more or\n * fewer than one key) throw rather than silently returning `undefined`.\n */\nimport { KNOWN_OPERATORS } from \"@ethisyscore/protocol\";\n\n/**\n * Resolution context for `var` references. Keys are looked up via dot-path\n * traversal; missing paths resolve to `undefined`.\n */\nexport type EvaluatorContext = Record<string, unknown>;\n\nconst KNOWN_OPERATOR_SET = new Set<string>(KNOWN_OPERATORS);\n\n/**\n * Evaluate a JsonLogic expression in the closed operator subset against the\n * supplied context.\n *\n * @param expr The expression tree. Scalars (`string | number | boolean | null`)\n * evaluate to themselves; arrays map element-wise; operator\n * objects must contain exactly one key from\n * {@link KNOWN_OPERATORS}.\n * @param context The context bag for `var` resolution. Dot-paths traverse\n * nested objects.\n *\n * @throws Error if `expr` contains an unknown operator, a function-typed value,\n * an operator object with a number of keys other than one, or any\n * other unsupported shape (e.g. `undefined`, `Symbol`, `bigint`).\n */\nexport function evaluate(expr: unknown, context: EvaluatorContext): unknown\n{\n // Literal scalars — return as-is.\n if (expr === null)\n {\n return null;\n }\n\n const t = typeof expr;\n if (t === \"string\" || t === \"number\" || t === \"boolean\")\n {\n return expr;\n }\n\n if (t === \"function\")\n {\n throw new Error(\"Function-typed values are not allowed in expressions.\");\n }\n\n if (Array.isArray(expr))\n {\n return expr.map((e) => evaluate(e, context));\n }\n\n if (t !== \"object\")\n {\n throw new Error(`Unsupported expression type: ${t}.`);\n }\n\n // Operator object — must have exactly one key from the closed vocabulary.\n const obj = expr as Record<string, unknown>;\n const keys = Object.keys(obj);\n if (keys.length !== 1)\n {\n throw new Error(\n `Expression object must have exactly one operator key (got ${keys.length}).`,\n );\n }\n\n const op = keys[0];\n if (!KNOWN_OPERATOR_SET.has(op))\n {\n throw new Error(`Unknown operator: ${op}.`);\n }\n\n const rawArgs = obj[op];\n\n // `var` takes a single dot-path argument and does not recurse over its operand.\n if (op === \"var\")\n {\n const path = typeof rawArgs === \"string\"\n ? rawArgs\n : Array.isArray(rawArgs) && typeof rawArgs[0] === \"string\"\n ? rawArgs[0]\n : \"\";\n return resolveVar(path, context);\n }\n\n const evaluatedArgs: unknown[] = Array.isArray(rawArgs)\n ? rawArgs.map((a) => evaluate(a, context))\n : [evaluate(rawArgs, context)];\n\n switch (op)\n {\n case \"==\": return evaluatedArgs[0] === evaluatedArgs[1];\n case \"!=\": return evaluatedArgs[0] !== evaluatedArgs[1];\n case \">\": return (evaluatedArgs[0] as number) > (evaluatedArgs[1] as number);\n case \"<\": return (evaluatedArgs[0] as number) < (evaluatedArgs[1] as number);\n case \">=\": return (evaluatedArgs[0] as number) >= (evaluatedArgs[1] as number);\n case \"<=\": return (evaluatedArgs[0] as number) <= (evaluatedArgs[1] as number);\n case \"and\": return evaluatedArgs.every(Boolean);\n case \"or\": return evaluatedArgs.some(Boolean);\n case \"not\": return !evaluatedArgs[0];\n case \"+\": return evaluatedArgs.reduce((acc: number, n) => acc + (n as number), 0);\n case \"-\": return (evaluatedArgs[0] as number) - (evaluatedArgs[1] as number);\n case \"*\": return evaluatedArgs.reduce((acc: number, n) => acc * (n as number), 1);\n case \"/\": return safeDivide(evaluatedArgs[0] as number, evaluatedArgs[1] as number);\n case \"%\": return safeModulo(evaluatedArgs[0] as number, evaluatedArgs[1] as number);\n default:\n // Unreachable: gated by KNOWN_OPERATOR_SET above. Kept for exhaustiveness.\n throw new Error(`Unknown operator: ${op}.`);\n }\n}\n\n/**\n * Throw on zero or non-finite denominators in `/`. JavaScript silently coerces\n * `1 / 0` to `Infinity`; that value then leaks into host-rendered props and\n * surfaces as weird UI (or React `<input value={Infinity} />` warnings). The\n * closed evaluator's fail-loud contract demands a real error so the host\n * surface mount can fall back to an `ErrorState`.\n */\nfunction safeDivide(a: number, b: number): number\n{\n if (b === 0 || !Number.isFinite(b))\n {\n throw new Error(\n `Division by ${b === 0 ? \"zero\" : String(b)} is not allowed in the closed evaluator.`,\n );\n }\n return a / b;\n}\n\n/**\n * Throw on zero or non-finite denominators in `%`. JavaScript silently coerces\n * `1 % 0` to `NaN`; that value would compare unequal to itself and corrupt\n * downstream comparisons in reactive rules.\n */\nfunction safeModulo(a: number, b: number): number\n{\n if (b === 0 || !Number.isFinite(b))\n {\n throw new Error(\n `Modulo by ${b === 0 ? \"zero\" : String(b)} is not allowed in the closed evaluator.`,\n );\n }\n return a % b;\n}\n\n/**\n * Resolve a dot-separated path against the supplied context, returning the\n * leaf value or `undefined` if any segment is missing or non-traversable.\n *\n * Empty paths resolve to `undefined`.\n */\nfunction resolveVar(path: string, context: EvaluatorContext): unknown\n{\n if (path === \"\")\n {\n return undefined;\n }\n\n const parts = path.split(\".\");\n let curr: unknown = context;\n for (const part of parts)\n {\n if (curr === null || typeof curr !== \"object\")\n {\n return undefined;\n }\n curr = (curr as Record<string, unknown>)[part];\n }\n return curr;\n}\n","import { createElement, type ReactElement, type ReactNode } from \"react\";\nimport type { ComponentRegistry } from \"./registry\";\nimport type { SduiNode } from \"./types\";\n\n/**\n * Walk a parsed SDUI tree and render it as a React element by looking up each\n * node's `type` in the supplied {@link ComponentRegistry}.\n *\n * The interpreter is purely structural:\n *\n * - It owns no UI styling, layout, or data fetching.\n * - It never reads `node.props` — props are forwarded opaquely to the host\n * component, which owns interpretation per primitive.\n * - It does not evaluate `node.bindings` — reactive rules are handled in a\n * separate task (E2.S2). For v1 the interpreter passes through the static\n * tree only.\n *\n * Each child is given a stable React `key` derived from its position so that\n * React's reconciler can identify list items across renders. The key is a\n * sibling-local index; the registry consumer is responsible for opting into a\n * stable identity if it has a domain-meaningful `props.key`.\n *\n * @throws Error when `node.type` is not present in the registry. This is the\n * fail-loud behaviour required by the closed v1 vocabulary — unknown\n * primitives must not silently degrade.\n */\nexport function interpret(node: SduiNode, registry: ComponentRegistry): ReactElement\n{\n const Component = registry[node.type];\n if (!Component)\n {\n throw new Error(`Unknown SDUI primitive: ${String(node.type)}`);\n }\n\n const children: ReactNode[] | undefined = node.children?.map(\n (child, index) => interpretChild(child, registry, index),\n );\n\n return createElement(Component, { props: node.props }, children);\n}\n\nfunction interpretChild(node: SduiNode, registry: ComponentRegistry, index: number): ReactElement\n{\n const Component = registry[node.type];\n if (!Component)\n {\n throw new Error(`Unknown SDUI primitive: ${String(node.type)}`);\n }\n\n const children: ReactNode[] | undefined = node.children?.map(\n (child, childIndex) => interpretChild(child, registry, childIndex),\n );\n\n return createElement(Component, { props: node.props, key: index }, children);\n}\n","/**\n * Semantic component registry for Contract B (worker remote-runtime)\n * extensions.\n *\n * Worker-side plugin code references UI primitives by name — e.g.\n * `<DataTable />`, `<Drawer />`, `<CanvasSurface />`. The host owns rendering:\n * a {@link SemanticComponentRegistry} maps each name to a concrete component\n * type, and the Remote DOM host receiver consults the registry on every\n * worker-emitted mutation. The closed primitive vocabulary {@link CONTRACT_B_PRIMITIVES}\n * is intentionally narrow — adding a primitive is a coordinated host change so\n * plugins cannot extend the contract ad hoc.\n *\n * **Alignment with SDUI:** Where the Contract B name overlaps with the\n * `@ethisyscore/protocol` SDUI primitive vocabulary (`DataTable`, `Form`), the\n * names are identical to avoid synonym drift. SDUI's `Action` is intentionally\n * NOT reused — Contract B's interactive primitive is named `Button` so the\n * worker authoring API stays close to React DOM idioms. The two vocabularies\n * may converge as they evolve, but the host registries (Contract A SDUI vs\n * Contract B Semantic) remain distinct surfaces today.\n *\n * The registry is renderer-agnostic — the generic `TComponent` parameter is\n * whatever component type the host supplies (React `ComponentType` in\n * production; plain objects in tests). This keeps the registry free of a\n * React peer-dep requirement and lets tests assert behaviour without rendering.\n */\n\n// ─── Closed v1 vocabulary ─────────────────────────────────────────────────────\n\n/**\n * The mandatory Contract B (worker remote-runtime) semantic primitive vocabulary.\n *\n * Adding a primitive requires a coordinated host change + protocol bump —\n * plugins cannot extend this set. Each name has a host-supplied concrete\n * component (gogo-ui in production); the registry enforces 1:1 substitution.\n */\nexport const CONTRACT_B_PRIMITIVES = [\n \"Button\",\n \"DataTable\",\n \"Form\",\n \"EntityPicker\",\n \"CommandBar\",\n \"Drawer\",\n \"Modal\",\n \"CanvasSurface\",\n \"WebGLSurface\",\n // Phase 1 additions — mirror the vite-plugin's CONTRACT_B_SEMANTIC_PRIMITIVES\n // list verbatim; drift between the two is caught by the\n // contract-b.test.ts \"import-map-allowlist matches extension-runtime\n // CONTRACT_B_PRIMITIVES\" test.\n \"Card\",\n \"Tabs\",\n \"Select\",\n \"Alert\",\n] as const;\n\n/**\n * Union type of the Contract B semantic primitive names — exported so callers\n * (tests, hosts) get full IntelliSense and compile-time exhaustiveness checks.\n */\nexport type SemanticPrimitiveName = typeof CONTRACT_B_PRIMITIVES[number];\n\nconst KNOWN_PRIMITIVE_SET: ReadonlySet<string> = new Set(CONTRACT_B_PRIMITIVES);\n\n// ─── Registry ─────────────────────────────────────────────────────────────────\n\n/**\n * Map of semantic primitive name → concrete component for Contract B\n * extensions.\n *\n * Construction is open-ended (callers register primitives one at a time) or\n * eager via {@link SemanticComponentRegistry.fromMap} which forces a complete\n * map up front and aborts if any primitive is missing.\n *\n * @typeParam TComponent - The host's concrete component shape. React hosts pass\n * `ComponentType<P>`; tests pass any tagged object.\n */\nexport class SemanticComponentRegistry<TComponent>\n{\n private readonly entries = new Map<SemanticPrimitiveName, TComponent>();\n\n /**\n * Register a primitive → component binding.\n *\n * Throws if `name` is not in {@link CONTRACT_B_PRIMITIVES} (drift prevention)\n * or if `name` is already registered (silent-override prevention).\n */\n public register(name: SemanticPrimitiveName, component: TComponent): void\n {\n if (!KNOWN_PRIMITIVE_SET.has(name))\n {\n throw new Error(\n `[component-registry] unknown semantic primitive '${name}'. ` +\n `Allowed: ${CONTRACT_B_PRIMITIVES.join(\", \")}.`,\n );\n }\n if (this.entries.has(name))\n {\n throw new Error(\n `[component-registry] primitive '${name}' is already registered. ` +\n `Construct a new registry instead of overriding a binding.`,\n );\n }\n this.entries.set(name, component);\n }\n\n /**\n * Resolve a primitive name to its concrete component. Throws if the name is\n * outside the Contract B vocabulary OR if the registry has no binding —\n * silent fallthrough would let typos render blank components, which is\n * worse than a fast failure for plugin authors.\n */\n public resolve(name: SemanticPrimitiveName): TComponent\n {\n if (!KNOWN_PRIMITIVE_SET.has(name))\n {\n throw new Error(\n `[component-registry] unknown semantic primitive '${name}'. ` +\n `Allowed: ${CONTRACT_B_PRIMITIVES.join(\", \")}.`,\n );\n }\n const found = this.entries.get(name);\n if (found === undefined)\n {\n const registered = this.listRegistered().join(\", \") || \"<none>\";\n throw new Error(\n `[component-registry] primitive '${name}' is not registered. ` +\n `Registered primitives: ${registered}.`,\n );\n }\n return found;\n }\n\n /**\n * Cheap presence probe — never throws. Use this when callers want to\n * fall back to a default component instead of erroring.\n */\n public has(name: SemanticPrimitiveName): boolean\n {\n return this.entries.has(name);\n }\n\n /**\n * Sorted list of registered primitive names. Stable output makes registry\n * diagnostics and snapshot tests deterministic.\n */\n public listRegistered(): SemanticPrimitiveName[]\n {\n return [...this.entries.keys()].sort() as SemanticPrimitiveName[];\n }\n\n /**\n * Build a registry from a complete `Record<SemanticPrimitiveName, T>` map.\n *\n * The `Record` type forces TypeScript to refuse incomplete literals at\n * compile time, AND we re-check at runtime so callers who type-erase the\n * record (e.g., via `as any`) still get a fast failure.\n */\n public static fromMap<T>(map: Record<SemanticPrimitiveName, T>): SemanticComponentRegistry<T>\n {\n const registry = new SemanticComponentRegistry<T>();\n const missing: SemanticPrimitiveName[] = [];\n for (const name of CONTRACT_B_PRIMITIVES)\n {\n const component = (map as Partial<Record<SemanticPrimitiveName, T>>)[name];\n if (component === undefined)\n {\n missing.push(name);\n continue;\n }\n registry.register(name, component);\n }\n if (missing.length > 0)\n {\n throw new Error(\n `[component-registry] fromMap is missing semantic primitive(s): ${missing.join(\", \")}.`,\n );\n }\n return registry;\n }\n}\n","/**\n * OffscreenCanvas transfer helpers + pointer/keyboard coalescing for\n * Contract B (worker remote-runtime) extensions.\n *\n * **Architecture.** Hosts that expose a {@link import(\"./component-registry\").SemanticComponentRegistry}'s\n * `CanvasSurface` / `WebGLSurface` primitive create a host-side `<canvas>`,\n * transfer control to the worker via {@link createOffscreenCanvasTransfer}, and\n * the plugin (running inside the worker realm) calls `offscreen.getContext()`\n * to render. The `<canvas>` is owned by the host DOM tree (so layout, theming,\n * and accessibility properties stay on the host side) but every pixel is\n * produced inside the worker — no per-frame `postMessage` cost, no main-thread\n * blocking.\n *\n * **Pointer / keyboard delivery.** Input events still originate on the host\n * `<canvas>`. To avoid spraying the worker port with per-event traffic the\n * host should wrap pointer-move / wheel / scroll callbacks in\n * {@link createInputEventCoalescer}, matching the trailing-edge coalescer\n * already documented in {@link import(\"./transport\").WorkerRemoteDomTransport}\n * (default 16ms ≈ one rAF). Keyboard events MUST use `discrete: true` —\n * coalescing them would drop characters.\n *\n * **Security.** No capability token, no MCP traffic, and no host-only globals\n * cross via the offscreen transfer. The transfer envelope is plain JSON with a\n * single `OffscreenCanvas` handle in the transfer list — the worker side\n * receives an opaque drawing surface and nothing else.\n */\n\n// ─── Public constants ────────────────────────────────────────────────────────\n\n/**\n * Default trailing-edge coalesce window for pointer / wheel / scroll events,\n * in milliseconds. 16ms ≈ one animation frame at 60Hz. Matches the default\n * documented in {@link import(\"./transport\").WorkerRemoteDomTransport}.\n */\nexport const DEFAULT_OFFSCREEN_COALESCE_MS = 16;\n\n// ─── Transfer envelope ───────────────────────────────────────────────────────\n\n/**\n * Host → worker envelope shape for an `OffscreenCanvas` transfer. Stable wire\n * contract — the worker side decodes by `type` and uses `surfaceId` to bind\n * the offscreen to the right host slot when a plugin renders multiple\n * canvases concurrently.\n */\nexport interface OffscreenTransferMessage\n{\n type: \"ethisys:offscreen:transfer\";\n surfaceId: string;\n width: number;\n height: number;\n offscreen: OffscreenCanvas;\n}\n\n/**\n * Construction options for {@link createOffscreenCanvasTransfer}.\n */\nexport interface OffscreenCanvasTransferOptions\n{\n /**\n * The host-owned `<canvas>` element. Its `width` / `height` are captured\n * before transfer so the worker sees the intended pixel dimensions even if\n * the host element resizes later (host should send a separate resize\n * message in that case).\n */\n canvas: HTMLCanvasElement;\n\n /**\n * Stable identifier the worker uses to route the offscreen to the matching\n * surface slot. Typically the SDUI node id of the `CanvasSurface` /\n * `WebGLSurface` primitive that the worker will draw into.\n */\n surfaceId: string;\n\n /**\n * Host-side postMessage function — typically the\n * {@link import(\"./transport\").WorkerRemoteDomTransport}'s port-side\n * `postMessage`. Tests inject a spy.\n */\n postMessage(message: OffscreenTransferMessage, transfer: Transferable[]): void;\n}\n\n/**\n * Successful transfer result. The caller retains a reference to the\n * `OffscreenCanvas` only for observability (e.g., to wire to the worker's\n * reply handshake) — direct rendering from the host side is forbidden because\n * control has already been transferred and any host-side draw call would\n * throw.\n */\nexport interface OffscreenCanvasTransferResult\n{\n offscreen: OffscreenCanvas;\n}\n\n/**\n * Module-scoped WeakSet tracking canvases this SDK has already transferred.\n *\n * Stored OUTSIDE the DOM node deliberately:\n *\n * - Writing an SDK expando (e.g. `__ethisysOffscreenTransferred`) onto a host-\n * owned `<canvas>` mutates state the host doesn't own. A WeakSet keeps the\n * bookkeeping on the SDK side and lets the canvas object stay pristine.\n * - WeakSet entries are collected when the canvas is GC'd, so the guard does\n * not leak memory for plugins that mount and unmount many surfaces.\n * - WeakSet membership is identity-based, so React/concurrent remount paths\n * that re-use the SAME node still see \"already transferred\"; a fresh DOM\n * node (always allocated by React when the key changes) is a fresh entry.\n */\nconst transferredCanvases: WeakSet<HTMLCanvasElement> = new WeakSet<HTMLCanvasElement>();\n\n/**\n * Transfer control of a host-owned `<canvas>` to the worker.\n *\n * Throws if:\n * - the environment lacks `HTMLCanvasElement.prototype.transferControlToOffscreen`\n * (the helper does NOT silently fall back — Contract B clients must require\n * the API and surface a clear error in unsupported browsers);\n * - the same canvas has already had control transferred (idempotency is the\n * caller's responsibility — re-transferring would throw in the browser\n * anyway, but we raise a clearer, diagnosable message ahead of that).\n */\nexport function createOffscreenCanvasTransfer(\n options: OffscreenCanvasTransferOptions,\n): OffscreenCanvasTransferResult\n{\n const { canvas, surfaceId, postMessage } = options;\n const proto = HTMLCanvasElement.prototype as unknown as {\n transferControlToOffscreen?: () => OffscreenCanvas;\n };\n if (typeof proto.transferControlToOffscreen !== \"function\")\n {\n throw new Error(\n \"[offscreen-canvas] OffscreenCanvas is not supported in this environment. \" +\n \"Contract B canvas surfaces require a browser with HTMLCanvasElement.transferControlToOffscreen.\",\n );\n }\n if (transferredCanvases.has(canvas))\n {\n throw new Error(\n `[offscreen-canvas] canvas already transferred (surfaceId='${surfaceId}'). ` +\n \"Create a fresh <canvas> for each surface instead of re-transferring.\",\n );\n }\n\n const offscreen = canvas.transferControlToOffscreen();\n transferredCanvases.add(canvas);\n\n const envelope: OffscreenTransferMessage = {\n type: \"ethisys:offscreen:transfer\",\n surfaceId,\n width: canvas.width,\n height: canvas.height,\n offscreen,\n };\n postMessage(envelope, [offscreen]);\n\n return { offscreen };\n}\n\n// ─── Coalescing ──────────────────────────────────────────────────────────────\n\n/**\n * Construction options for {@link createInputEventCoalescer}.\n */\nexport interface InputEventCoalescerOptions\n{\n /**\n * Coalesce window in milliseconds. Ignored when {@link discrete} is true.\n * Defaults to {@link DEFAULT_OFFSCREEN_COALESCE_MS} (16ms — one rAF).\n */\n coalesceMs?: number;\n\n /**\n * When `true`, forward every payload synchronously without coalescing.\n * Use this for keyboard events — coalescing would drop characters by\n * collapsing multiple key strokes into one trailing delivery.\n */\n discrete?: boolean;\n}\n\n/**\n * Build a trailing-edge input-event coalescer. The pattern matches\n * {@link import(\"./transport\").WorkerRemoteDomTransport.createCoalescer} —\n * factored out here so OffscreenCanvas callers don't need to construct a full\n * transport just to coalesce pointer-move events.\n *\n * Trailing-edge semantics: every burst within a coalesce window collapses to\n * a single delivery carrying the **last** payload seen in the window. A new\n * payload arriving after a window has flushed starts a fresh window.\n */\nexport function createInputEventCoalescer<T>(\n sink: (payload: T) => void,\n options: InputEventCoalescerOptions = {},\n): (payload: T) => void\n{\n if (options.discrete === true)\n {\n // Discrete mode: pass through unchanged. Keyboard input belongs here.\n return (payload: T) =>\n {\n sink(payload);\n };\n }\n const coalesceMs = options.coalesceMs ?? DEFAULT_OFFSCREEN_COALESCE_MS;\n let pending: T | undefined;\n let pendingSet = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const flush = (): void =>\n {\n timer = undefined;\n if (!pendingSet)\n {\n return;\n }\n const value = pending as T;\n pending = undefined;\n pendingSet = false;\n sink(value);\n };\n\n return (payload: T) =>\n {\n pending = payload;\n pendingSet = true;\n if (timer === undefined)\n {\n timer = setTimeout(flush, coalesceMs);\n }\n };\n}\n","/**\n * Wire-shape constants and type guards for host→plugin bridge push messages\n * (WI 4858 sub-plan 2). These types ride the existing MessagePort channel\n * alongside the `ethisys:mcp:*` and `ethisys:remotedom` envelopes — the\n * `type` discriminant keeps them fully separate at the dispatch site.\n *\n * The host posts bridge pushes through `WorkerRemoteDomTransport.hostPort`;\n * the plugin side reads them in `createPortBridgeClient` (plugin-ui package).\n * In-realm (Tier T) plugins use `InMemoryBridgeTransport` which calls the\n * subscriber callbacks directly — no serialisation needed.\n */\n\n// ── Type constants ───────────────────────────────────────────────────────────\n\nexport const BRIDGE_PUSH_THEME = \"ethisys:bridge:theme\" as const;\nexport const BRIDGE_PUSH_LOCALE = \"ethisys:bridge:locale\" as const;\nexport const BRIDGE_PUSH_DENSITY = \"ethisys:bridge:density\" as const;\nexport const BRIDGE_PUSH_A11Y = \"ethisys:bridge:a11y\" as const;\nexport const BRIDGE_NAV_PUSH = \"ethisys:bridge:nav\" as const;\nexport const BRIDGE_CHROME_REQUEST = \"ethisys:bridge:chrome:request\" as const;\nexport const BRIDGE_CHROME_RESPONSE = \"ethisys:bridge:chrome:response\" as const;\nexport const BRIDGE_SESSION_TOKEN_PUSH = \"ethisys:bridge:token\" as const;\nexport const BRIDGE_LIFECYCLE_EVENT = \"ethisys:bridge:lifecycle\" as const;\nexport const BRIDGE_A11Y_ANNOUNCE = \"ethisys:bridge:a11y:announce\" as const;\n\n// ── Envelope shapes ──────────────────────────────────────────────────────────\n\n/** Theme push: host → plugin whenever the host theme changes. */\nexport interface BridgePushThemeEnvelope {\n readonly type: typeof BRIDGE_PUSH_THEME;\n /** \"light\" | \"dark\" | \"high-contrast\" */\n readonly mode: string;\n /** Flat design-token map (CSS-variable-name → value). May be empty. */\n readonly tokens: Readonly<Record<string, string>>;\n}\n\n/** Locale push: host → plugin whenever the active locale changes. */\nexport interface BridgePushLocaleEnvelope {\n readonly type: typeof BRIDGE_PUSH_LOCALE;\n /** BCP 47 tag, e.g. \"en-GB\". */\n readonly locale: string;\n /** \"ltr\" | \"rtl\" */\n readonly dir: \"ltr\" | \"rtl\";\n}\n\n/** Density push: host → plugin when the UI density preference changes. */\nexport interface BridgePushDensityEnvelope {\n readonly type: typeof BRIDGE_PUSH_DENSITY;\n /** \"comfortable\" | \"compact\" */\n readonly density: string;\n}\n\n/** A11y push: host → plugin when accessibility prefs change. */\nexport interface BridgePushA11yEnvelope {\n readonly type: typeof BRIDGE_PUSH_A11Y;\n readonly reducedMotion: boolean;\n readonly highContrast: boolean;\n}\n\n/** Nav push: host → plugin when the SPA location changes. */\nexport interface BridgeNavPushEnvelope {\n readonly type: typeof BRIDGE_NAV_PUSH;\n /** Current SPA path (pathname + search). */\n readonly path: string;\n /** `window.history.length` at the time of push. */\n readonly historyLength: number;\n}\n\n/** Plugin → host: request a chrome action (e.g. toast, modal). */\nexport interface BridgeChromeRequestEnvelope {\n readonly type: typeof BRIDGE_CHROME_REQUEST;\n /** Correlation id — matched against the response. */\n readonly id: string;\n /** \"toast\" | \"confirm\" | \"openUrl\" */\n readonly action: string;\n readonly payload: Readonly<Record<string, unknown>>;\n}\n\n/** Host → plugin: response to a chrome request. */\nexport interface BridgeChromeResponseEnvelope {\n readonly type: typeof BRIDGE_CHROME_RESPONSE;\n readonly id: string;\n readonly ok: boolean;\n readonly result?: unknown;\n readonly error?: string;\n}\n\n/** Host → plugin: push a short-lived frontend-session token. */\nexport interface BridgeSessionTokenPushEnvelope {\n readonly type: typeof BRIDGE_SESSION_TOKEN_PUSH;\n /** Opaque JWT string — audience-restricted to the plugin's own backend. */\n readonly token: string;\n /** Absolute epoch-ms at which the token expires. Refresh fires 30 s before. */\n readonly expiresAtMs: number;\n}\n\n/** Host ↔ plugin lifecycle event. */\nexport type BridgeLifecyclePhase = \"load\" | \"ready\" | \"error\" | \"unload\";\n\nexport interface BridgeLifecycleEventEnvelope {\n readonly type: typeof BRIDGE_LIFECYCLE_EVENT;\n readonly phase: BridgeLifecyclePhase;\n /** Present when phase === \"error\". */\n readonly message?: string;\n}\n\n/** Plugin → host: a11y live-region announcement request. */\nexport interface BridgeA11yAnnounceEnvelope {\n readonly type: typeof BRIDGE_A11Y_ANNOUNCE;\n readonly message: string;\n /** \"polite\" | \"assertive\" */\n readonly politeness: \"polite\" | \"assertive\";\n}\n\n// ── Type guards ──────────────────────────────────────────────────────────────\n\nfunction isObj(v: unknown): v is Record<string, unknown> {\n return v !== null && typeof v === \"object\";\n}\n\nexport function isBridgePushTheme(v: unknown): v is BridgePushThemeEnvelope {\n return isObj(v) && v[\"type\"] === BRIDGE_PUSH_THEME && typeof v[\"mode\"] === \"string\";\n}\n\nexport function isBridgePushLocale(v: unknown): v is BridgePushLocaleEnvelope {\n return isObj(v) && v[\"type\"] === BRIDGE_PUSH_LOCALE && typeof v[\"locale\"] === \"string\";\n}\n\nexport function isBridgePushDensity(v: unknown): v is BridgePushDensityEnvelope {\n return isObj(v) && v[\"type\"] === BRIDGE_PUSH_DENSITY && typeof v[\"density\"] === \"string\";\n}\n\nexport function isBridgePushA11y(v: unknown): v is BridgePushA11yEnvelope {\n return isObj(v) && v[\"type\"] === BRIDGE_PUSH_A11Y\n && typeof v[\"reducedMotion\"] === \"boolean\"\n && typeof v[\"highContrast\"] === \"boolean\";\n}\n\nexport function isBridgeNavPush(v: unknown): v is BridgeNavPushEnvelope {\n return isObj(v) && v[\"type\"] === BRIDGE_NAV_PUSH && typeof v[\"path\"] === \"string\";\n}\n\nexport function isBridgeChromeRequest(v: unknown): v is BridgeChromeRequestEnvelope {\n return isObj(v) && v[\"type\"] === BRIDGE_CHROME_REQUEST && typeof v[\"id\"] === \"string\";\n}\n\nexport function isBridgeChromeResponse(v: unknown): v is BridgeChromeResponseEnvelope {\n return isObj(v) && v[\"type\"] === BRIDGE_CHROME_RESPONSE && typeof v[\"id\"] === \"string\";\n}\n\nexport function isBridgeSessionTokenPush(v: unknown): v is BridgeSessionTokenPushEnvelope {\n return isObj(v) && v[\"type\"] === BRIDGE_SESSION_TOKEN_PUSH\n && typeof v[\"token\"] === \"string\"\n && typeof v[\"expiresAtMs\"] === \"number\";\n}\n\nexport function isBridgeLifecycleEvent(v: unknown): v is BridgeLifecycleEventEnvelope {\n return isObj(v) && v[\"type\"] === BRIDGE_LIFECYCLE_EVENT && typeof v[\"phase\"] === \"string\";\n}\n\nexport function isBridgeA11yAnnounce(v: unknown): v is BridgeA11yAnnounceEnvelope {\n return isObj(v) && v[\"type\"] === BRIDGE_A11Y_ANNOUNCE && typeof v[\"message\"] === \"string\";\n}\n","/**\n * Worker-side host transport for `renderMode: remote-runtime` extensions\n * (Contract B). Owns the lifecycle of:\n *\n * 1. A path-pinned module `Worker` spawned from a host-issued bootstrap URL.\n * 2. A `MessagePort`-based RPC channel (handed to the worker on spawn).\n * 3. A Shopify Remote DOM root receiver — wired here so the worker's UI\n * tree mounts into a host React tree.\n * 4. Event coalescing (default ≤16ms, one rAF) for high-frequency input\n * events before they cross the port.\n * 5. MCP tool/resource calls forwarded through the port — but **the\n * capability token never crosses the boundary**. The token is bound\n * to the worker scope on the HOST side: the host transport intercepts\n * the plugin's MCP request, attaches the token to the outbound HTTP\n * call, and forwards ONLY the response payload back through the port.\n *\n * This module is intentionally framework-thin: callers wire `mount(root)`\n * onto a React tree (typically inside a `<WorkerSurfaceMount>`), and the\n * Remote DOM receiver is responsible for translating worker-side\n * RemoteRoot mutations into host-side component renders.\n */\n\nimport { createOffscreenCanvasTransfer } from \"./offscreen\";\nimport {\n BRIDGE_PUSH_THEME,\n BRIDGE_PUSH_LOCALE,\n BRIDGE_PUSH_DENSITY,\n BRIDGE_PUSH_A11Y,\n BRIDGE_NAV_PUSH,\n BRIDGE_CHROME_REQUEST,\n BRIDGE_A11Y_ANNOUNCE,\n BRIDGE_LIFECYCLE_EVENT,\n type BridgePushThemeEnvelope,\n type BridgePushLocaleEnvelope,\n type BridgePushDensityEnvelope,\n type BridgePushA11yEnvelope,\n type BridgeNavPushEnvelope,\n} from \"./bridge-envelopes\";\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\n/**\n * Structural subset of the DOM `Worker` interface that the transport needs.\n * Tests inject a fake; production callers pass the global `Worker`.\n */\nexport interface WorkerLike\n{\n onmessage: ((ev: MessageEvent) => void) | null;\n onerror: ((ev: ErrorEvent) => void) | null;\n onmessageerror: ((ev: MessageEvent) => void) | null;\n postMessage(message: unknown, transfer?: Transferable[]): void;\n terminate(): void;\n}\n\n/**\n * Constructor signature compatible with the DOM `Worker` constructor. The\n * `workerCtor` option exists exclusively so tests can swap in a fake — in\n * production the caller passes the global `Worker`.\n */\nexport type WorkerCtor = new (url: string | URL, options?: WorkerOptions) => WorkerLike;\n\n/**\n * Discriminated union of MCP fetch shapes the host transport issues. The\n * `capabilityToken` is attached on the host side ONLY — it is never present\n * in any message that crosses the worker port.\n */\nexport type McpHttpRequest =\n | {\n kind: \"invokeTool\";\n name: string;\n args: unknown;\n capabilityToken: string;\n signal?: AbortSignal;\n }\n | {\n kind: \"getResource\";\n uri: string;\n capabilityToken: string;\n signal?: AbortSignal;\n };\n\n/**\n * Host-side HTTP client for MCP calls. Implementations are responsible for\n * carrying the supplied `capabilityToken` on the outbound request (typically\n * via `Authorization: Bearer`).\n */\nexport interface McpHttpClient\n{\n fetch(request: McpHttpRequest): Promise<{ ok: boolean; data?: unknown; error?: string }>;\n}\n\n/**\n * Construction options for {@link WorkerRemoteDomTransport}.\n */\nexport interface WorkerRemoteDomTransportOptions\n{\n /**\n * Canonical host-origin URL for the worker bootstrap script. This MUST\n * be supplied by the host — never built from user input or extension\n * manifest values — so the worker spawn is path-pinned.\n */\n bootstrapUrl: string;\n\n /**\n * Mints (or fetches a cached) capability token. The host transport\n * resolves this for each outbound MCP HTTP call. The token NEVER leaves\n * host scope — plugin code (the worker side) cannot read it.\n */\n capabilityToken: () => Promise<string>;\n\n /**\n * Host-side MCP HTTP client. The transport bridges port-side plugin\n * MCP requests to this client and returns only the response payload\n * back through the port.\n */\n mcpClient: McpHttpClient;\n\n /**\n * Injectable Worker constructor (tests use a fake). Defaults to the\n * global `Worker`.\n */\n workerCtor?: WorkerCtor;\n\n /**\n * Coalescing window in milliseconds for high-frequency events. Defaults\n * to 16ms (~one animation frame). Trailing-edge: the most recent payload\n * within the window is delivered after the window elapses.\n */\n coalesceMs?: number;\n\n /**\n * Maximum number of MCP requests (combined `invokeTool` + `getResource`)\n * the transport will forward to {@link mcpClient} concurrently. Defaults\n * to 8. Requests above the cap are rejected with a deterministic error\n * reply over the port — the worker MUST handle that response shape.\n *\n * Bounded concurrency is a back-pressure boundary, not a quota: it stops\n * a compromised or buggy worker from saturating the host's HTTP / token\n * pool. Production hosts may want to lower this in multi-tenant pools\n * where many extensions share one host process.\n */\n maxConcurrentMcpRequests?: number;\n}\n\n/**\n * Wire shape of a host-sourced input event forwarded to the worker over the\n * port. Pointer / wheel / keyboard payloads share a single envelope type so\n * the worker has one inbound dispatch site. The transport is structurally\n * agnostic to the payload — it forwards verbatim.\n *\n * `kind` discriminates the event family. Coordinates are CSS pixels relative\n * to the host canvas; coalescing happens host-side before the call to\n * {@link WorkerRemoteDomTransport.postInputEvent}.\n */\nexport type InputEventPayload =\n | {\n kind: \"pointermove\" | \"pointerdown\" | \"pointerup\" | \"pointercancel\";\n surfaceId: string;\n x: number;\n y: number;\n buttons: number;\n pointerType: string;\n }\n | {\n kind: \"wheel\";\n surfaceId: string;\n deltaX: number;\n deltaY: number;\n deltaMode: number;\n }\n | {\n kind: \"keydown\" | \"keyup\";\n surfaceId: string;\n key: string;\n code: string;\n ctrlKey: boolean;\n shiftKey: boolean;\n altKey: boolean;\n metaKey: boolean;\n };\n\n/**\n * Documented wire protocol identifier embedded in handshake / message\n * envelopes. Bumped when the worker ↔ host message shape changes in a\n * backward-incompatible way.\n */\nexport const WORKER_TRANSPORT_PROTOCOL = \"ethisys.worker.remotedom.v1\";\n\n/**\n * Wire shape of the handshake message the host posts to the worker on\n * {@link WorkerRemoteDomTransport.connect}. Locked here so the SDK side and the\n * API-hosted bootstrap script (`WorkerBootstrapScriptProvider`) read from the\n * same field names — see the API tests pinning `event.data.moduleUrl` and\n * `event.data.importMap`.\n *\n * The handshake is the SOLE message that ever carries `moduleUrl` or\n * `importMap`; subsequent port traffic uses the discriminated message types\n * declared below. The capability token NEVER appears in this payload (or any\n * other postMessage payload) — it is bound on the host side via\n * {@link WorkerRemoteDomTransportOptions.capabilityToken}.\n */\nexport interface WorkerHandshakePayload\n{\n readonly type: \"ethisys:worker:handshake\";\n readonly protocol: string;\n /** Host-origin URL of the plugin's worker bundle entry. */\n readonly moduleUrl: string;\n /**\n * Frozen bare-specifier → host-origin URL map that the bootstrap installs\n * before importing {@link moduleUrl}. Pulled from the plugin's\n * `worker-bundle.import-map.json` at runtime by the host mount.\n */\n readonly importMap: Readonly<Record<string, string>>;\n}\n\n// ─── Implementation ───────────────────────────────────────────────────────────\n\ninterface InvokeToolMessage\n{\n id: string;\n type: \"ethisys:mcp:invokeTool\";\n name: string;\n args: unknown;\n}\n\ninterface GetResourceMessage\n{\n id: string;\n type: \"ethisys:mcp:getResource\";\n uri: string;\n}\n\ninterface RemoteDomMessage\n{\n type: \"ethisys:remotedom\";\n payload: unknown;\n}\n\ntype InboundMessage = InvokeToolMessage | GetResourceMessage | RemoteDomMessage | { type: string; [k: string]: unknown };\n\n/**\n * Worker-side host transport for Contract B extensions.\n *\n * Construction immediately spawns the worker and transfers one end of a\n * fresh `MessageChannel`; the host retains `port1`, the worker receives\n * `port2`. Both sides exchange messages via their port and the worker port\n * is the *only* channel for plugin ↔ host traffic after handshake.\n */\n/**\n * Default cap for in-flight MCP requests forwarded by the transport. Chosen to\n * cover typical interactive UI traffic (dashboards, list views, detail panels)\n * without letting a runaway worker saturate the host's HTTP / token pool.\n */\nexport const DEFAULT_MAX_CONCURRENT_MCP_REQUESTS = 8;\n\nexport class WorkerRemoteDomTransport\n{\n private readonly worker: WorkerLike;\n private readonly hostPort: MessagePort;\n private readonly workerPort: MessagePort;\n private readonly mcpClient: McpHttpClient;\n private readonly capabilityTokenProvider: () => Promise<string>;\n private readonly coalesceMs: number;\n private readonly maxConcurrentMcpRequests: number;\n private readonly abortController: AbortController;\n private inFlightMcpRequests = 0;\n private remoteDomConsumer: ((payload: unknown) => void) | undefined;\n private bridgeMessageConsumer: ((msg: unknown) => void) | undefined;\n private disposed = false;\n private connected = false;\n\n public constructor(options: WorkerRemoteDomTransportOptions)\n {\n const WorkerCtor = options.workerCtor ?? (globalThis as unknown as { Worker: WorkerCtor }).Worker;\n if (!WorkerCtor)\n {\n throw new Error(\"WorkerRemoteDomTransport: no Worker constructor available\");\n }\n\n this.mcpClient = options.mcpClient;\n this.capabilityTokenProvider = options.capabilityToken;\n this.coalesceMs = options.coalesceMs ?? 16;\n this.maxConcurrentMcpRequests = Math.max(\n 1,\n options.maxConcurrentMcpRequests ?? DEFAULT_MAX_CONCURRENT_MCP_REQUESTS,\n );\n // Aborted from dispose() — propagates into the mcpClient.fetch path so\n // already-in-flight HTTP calls can short-circuit instead of resolving\n // into a closed port.\n this.abortController = new AbortController();\n\n // Spawn the worker FIRST so the constructor's spawn shape is\n // observable to tests even if MessageChannel construction throws.\n this.worker = new WorkerCtor(options.bootstrapUrl, { type: \"module\" });\n\n const channel = new MessageChannel();\n this.hostPort = channel.port1;\n this.workerPort = channel.port2;\n\n // Wire host-side port handling before {@link connect} posts the\n // handshake — this closes a race where the worker could reply faster\n // than we wire up.\n this.hostPort.onmessage = (ev) => this.handlePortMessage(ev.data as InboundMessage);\n this.hostPort.start();\n\n this.worker.onerror = (ev) =>\n {\n // Defensive: do NOT echo worker errors back to the worker — they\n // may carry plugin-side details. Host-side observability is the\n // platform's responsibility.\n void ev;\n };\n }\n\n /**\n * Post the handshake to the worker. Idempotent — only the FIRST call\n * transfers the {@link MessagePort} and posts the handshake payload;\n * subsequent calls are silent no-ops. Splitting this off from the\n * constructor lets the host mount fetch the per-plugin\n * `worker-bundle.import-map.json` (and resolve the bundle's module URL)\n * before the bootstrap script consumes them.\n *\n * Wire shape: {@link WorkerHandshakePayload}. The capability token NEVER\n * appears in the payload — it's bound on the host side via the\n * {@link WorkerRemoteDomTransportOptions.capabilityToken} provider.\n *\n * The `moduleUrl` and `importMap` are forwarded to the worker so the\n * bootstrap script (served from the host-pinned\n * `/extensions/runtime/worker-bootstrap.js`) can:\n * 1. Compose the frozen, same-origin-validated `IMPORT_MAP` from the\n * handshake payload (rejecting any cross-origin entries).\n * 2. `safeImport(moduleUrl)` the plugin's entry module.\n *\n * Same-origin enforcement is the bootstrap's job — this transport is\n * structurally agnostic to the host origin and only forwards what it's\n * handed.\n */\n public connect(moduleUrl: string, importMap: Record<string, string>): void\n {\n if (this.connected)\n {\n return;\n }\n this.connected = true;\n\n // Snapshot the import map into a fresh object so the worker handshake\n // is decoupled from caller-side mutation. `Object.freeze` here is\n // defence-in-depth — the worker rebuilds with `Object.create(null)`\n // anyway, but freezing the host-side payload makes mutation a typed\n // error for any host code that hangs onto the reference.\n const handshake: WorkerHandshakePayload = Object.freeze({\n type: \"ethisys:worker:handshake\",\n protocol: WORKER_TRANSPORT_PROTOCOL,\n moduleUrl,\n importMap: Object.freeze({ ...importMap }),\n });\n this.worker.postMessage(handshake, [this.workerPort]);\n }\n\n /**\n * Bind a consumer for Remote DOM mutation payloads emitted by the worker.\n * The Remote DOM receiver wiring is owned by the caller (typically a host\n * React component); this method exposes the raw stream so the receiver\n * can integrate cleanly without a circular package import.\n */\n public onRemoteDom(consumer: (payload: unknown) => void): void\n {\n this.remoteDomConsumer = consumer;\n }\n\n /**\n * Transfer control of a host-owned `<canvas>` to the worker so plugin code\n * can render via `OffscreenCanvas`. The host retains the `<canvas>` for\n * layout / accessibility purposes only — every pixel is produced inside the\n * worker, so the main thread is never blocked per frame.\n *\n * The transfer rides the established MessagePort (not the worker global\n * `postMessage`) so it is multiplexed with the rest of the host ↔ worker\n * traffic on the same channel. The `OffscreenCanvas` handle is the sole\n * `Transferable` in the envelope; no capability token, MCP context, or\n * other host-only data crosses the boundary.\n *\n * Throws if the environment lacks `transferControlToOffscreen()` or if the\n * canvas has already been transferred — see {@link createOffscreenCanvasTransfer}\n * for the exact diagnostics.\n */\n public transferCanvas(\n canvas: HTMLCanvasElement,\n options: { surfaceId: string },\n ): { offscreen: OffscreenCanvas }\n {\n return createOffscreenCanvasTransfer({\n canvas,\n surfaceId: options.surfaceId,\n postMessage: (message, transfer) =>\n {\n this.hostPort.postMessage(message, transfer);\n },\n });\n }\n\n /**\n * Forward a host-sourced input event to the worker over the port. The\n * payload shape is opaque to the transport — pointer / wheel / keyboard\n * envelopes share the same wire type. Callers are expected to wrap\n * high-frequency (pointer-move, wheel, scroll) events in\n * {@link createCoalescer} or {@link createInputEventCoalescer} BEFORE\n * calling this method so the port never sees the un-coalesced flood.\n *\n * Keyboard / pointer-down / pointer-up events are discrete and should be\n * delivered without coalescing — pass them straight through.\n */\n public postInputEvent(payload: InputEventPayload): void\n {\n this.hostPort.postMessage({\n type: \"ethisys:input:event\",\n payload,\n });\n }\n\n /**\n * Build a trailing-edge coalescer keyed to {@link WorkerRemoteDomTransportOptions.coalesceMs}.\n *\n * Use it to wrap pointer-move / scroll / resize callbacks **before**\n * they cross the port. The last payload in any coalescing window is the\n * one that wins.\n */\n public createCoalescer<T>(consumer: (payload: T) => void): (payload: T) => void\n {\n let pending: T | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n const flush = () =>\n {\n timer = undefined;\n const value = pending;\n pending = undefined;\n if (value !== undefined)\n {\n consumer(value);\n }\n };\n return (payload: T) =>\n {\n pending = payload;\n if (timer === undefined)\n {\n timer = setTimeout(flush, this.coalesceMs);\n }\n };\n }\n\n // ─── Bridge push methods (WI 4858 sub-plan 2) ─────────────────────────────\n\n /**\n * Register a consumer for inbound plugin→host bridge messages\n * (chrome requests, a11y announce, lifecycle events). Only the most recently\n * registered consumer is kept — the host mount wires exactly one.\n */\n public onBridgeMessage(consumer: (msg: unknown) => void): void\n {\n this.bridgeMessageConsumer = consumer;\n }\n\n /**\n * Push the current host theme to the plugin worker. Safe to call on every\n * host theme-context change — the transport coalesces nothing here; rate\n * limiting at the call site is the host's responsibility.\n */\n public pushTheme(payload: Omit<BridgePushThemeEnvelope, \"type\">): void\n {\n this.safePostMessage({ type: BRIDGE_PUSH_THEME, ...payload });\n }\n\n /** Push the current host locale and text direction to the plugin worker. */\n public pushLocale(payload: Omit<BridgePushLocaleEnvelope, \"type\">): void\n {\n this.safePostMessage({ type: BRIDGE_PUSH_LOCALE, ...payload });\n }\n\n /** Push the current UI density preference to the plugin worker. */\n public pushDensity(payload: Omit<BridgePushDensityEnvelope, \"type\">): void\n {\n this.safePostMessage({ type: BRIDGE_PUSH_DENSITY, ...payload });\n }\n\n /** Push updated accessibility preferences to the plugin worker. */\n public pushA11y(payload: Omit<BridgePushA11yEnvelope, \"type\">): void\n {\n this.safePostMessage({ type: BRIDGE_PUSH_A11Y, ...payload });\n }\n\n /** Push the current SPA navigation state to the plugin worker. */\n public pushNav(payload: Omit<BridgeNavPushEnvelope, \"type\">): void\n {\n this.safePostMessage({ type: BRIDGE_NAV_PUSH, ...payload });\n }\n\n /**\n * Tear down the worker and port. Idempotent.\n *\n * Order matters: we abort BEFORE closing the port so any in-flight\n * `mcpClient.fetch` that observes the signal short-circuits and the\n * subsequent attempt to post a reply lands in the disposed-guard branch\n * (which silently drops the post) instead of throwing on a closed port.\n */\n public dispose(): void\n {\n if (this.disposed)\n {\n return;\n }\n this.disposed = true;\n try\n {\n this.abortController.abort();\n }\n catch\n {\n // AbortController.abort() shouldn't throw, but never let dispose\n // crash the caller — teardown must be best-effort across all\n // resources.\n }\n try\n {\n this.hostPort.close();\n }\n catch\n {\n // ignore — port may already be closed\n }\n try\n {\n this.worker.terminate();\n }\n catch\n {\n // ignore\n }\n }\n\n /**\n * Best-effort wrapper around `hostPort.postMessage` that tolerates posts\n * arriving after {@link dispose}. The browser throws on a closed port and\n * the async handlers below can race dispose, so any post initiated by an\n * awaited continuation must be guarded.\n */\n private safePostMessage(message: unknown): void\n {\n if (this.disposed)\n {\n return;\n }\n try\n {\n this.hostPort.postMessage(message);\n }\n catch\n {\n // Port may have been closed between the disposed check and the\n // post call (browser closes ports asynchronously). Swallowing the\n // throw here is safe because we are already on a teardown path.\n }\n }\n\n // ─── Port message handling ──────────────────────────────────────────────\n\n private handlePortMessage(message: InboundMessage): void\n {\n // Post-dispose port traffic is dropped silently. The browser closes\n // MessagePorts asynchronously; any handler invocation after dispose\n // is a race we observe-and-skip rather than try to service.\n if (this.disposed)\n {\n return;\n }\n switch (message.type)\n {\n case \"ethisys:mcp:invokeTool\":\n this.dispatchMcp(message as InvokeToolMessage, \"ethisys:mcp:invokeTool:result\", (m) => this.handleInvokeTool(m));\n return;\n\n case \"ethisys:mcp:getResource\":\n this.dispatchMcp(message as GetResourceMessage, \"ethisys:mcp:getResource:result\", (m) => this.handleGetResource(m));\n return;\n\n case \"ethisys:remotedom\":\n this.remoteDomConsumer?.((message as RemoteDomMessage).payload);\n return;\n\n case BRIDGE_CHROME_REQUEST:\n case BRIDGE_A11Y_ANNOUNCE:\n case BRIDGE_LIFECYCLE_EVENT:\n this.bridgeMessageConsumer?.(message);\n return;\n\n default:\n // Unknown messages are ignored. The host MUST NOT echo unknown\n // shapes back to the worker — that would create an attacker-\n // controlled reflection surface.\n return;\n }\n }\n\n /**\n * Gate inbound MCP requests through the bounded concurrency window,\n * reject with a deterministic error reply when the cap is exceeded, and\n * decrement the counter unconditionally when the underlying handler\n * settles (regardless of resolution/rejection shape).\n */\n private dispatchMcp<T extends { id: string }>(\n message: T,\n resultType: string,\n handler: (m: T) => Promise<void>,\n ): void\n {\n if (this.inFlightMcpRequests >= this.maxConcurrentMcpRequests)\n {\n // Back-pressure boundary. The worker MUST surface this to plugin\n // code so it can retry / degrade gracefully rather than silently\n // hang on a never-resolving promise.\n this.safePostMessage({\n id: message.id,\n type: resultType,\n ok: false,\n error: `MCP back-pressure: in-flight cap of ${this.maxConcurrentMcpRequests} reached.`,\n });\n return;\n }\n this.inFlightMcpRequests++;\n handler(message).finally(() =>\n {\n this.inFlightMcpRequests = Math.max(0, this.inFlightMcpRequests - 1);\n });\n }\n\n private async handleInvokeTool(message: InvokeToolMessage): Promise<void>\n {\n let token: string;\n try\n {\n token = await this.capabilityTokenProvider();\n }\n catch (err)\n {\n this.replyError(message.id, \"ethisys:mcp:invokeTool:result\", err);\n return;\n }\n // Disposed-before-token: short-circuit. The token mint may have\n // resolved seconds after dispose; posting the reply now would race\n // a closed port.\n if (this.disposed)\n {\n return;\n }\n try\n {\n const result = await this.mcpClient.fetch({\n kind: \"invokeTool\",\n name: message.name,\n args: message.args,\n capabilityToken: token,\n signal: this.abortController.signal,\n });\n // CRITICAL: response payload must never include the token. We\n // forward ONLY the public-facing fields the plugin needs.\n this.safePostMessage({\n id: message.id,\n type: \"ethisys:mcp:invokeTool:result\",\n ok: result.ok,\n data: result.data,\n error: result.error,\n });\n }\n catch (err)\n {\n this.replyError(message.id, \"ethisys:mcp:invokeTool:result\", err);\n }\n }\n\n private async handleGetResource(message: GetResourceMessage): Promise<void>\n {\n let token: string;\n try\n {\n token = await this.capabilityTokenProvider();\n }\n catch (err)\n {\n this.replyError(message.id, \"ethisys:mcp:getResource:result\", err);\n return;\n }\n if (this.disposed)\n {\n return;\n }\n try\n {\n const result = await this.mcpClient.fetch({\n kind: \"getResource\",\n uri: message.uri,\n capabilityToken: token,\n signal: this.abortController.signal,\n });\n this.safePostMessage({\n id: message.id,\n type: \"ethisys:mcp:getResource:result\",\n ok: result.ok,\n data: result.data,\n error: result.error,\n });\n }\n catch (err)\n {\n this.replyError(message.id, \"ethisys:mcp:getResource:result\", err);\n }\n }\n\n private replyError(id: string, type: string, err: unknown): void\n {\n // Surface a sanitised error string — never the raw error object\n // (which may carry stack traces or token fragments from a misbehaving\n // mcpClient implementation). Routed through safePostMessage so a\n // post-dispose reply does not crash on a closed port.\n const message = err instanceof Error ? err.message : \"MCP request failed\";\n this.safePostMessage({ id, type, ok: false, error: message });\n }\n}\n","/**\n * Cross-origin iframe bridge transport (WI 4858 sub-plan 5, Tier U).\n *\n * The host-side counterpart to a `TENANT_APP` plugin rendered in a cross-origin\n * `<iframe sandbox>`. It brokers the SAME `ethisys:mcp:*` request/reply shapes\n * and host-side capability-token brokering as {@link WorkerRemoteDomTransport}\n * (the capability JWT NEVER crosses the boundary), but the transport is a\n * cross-origin iframe rather than a Worker, so it adds:\n * - a per-mount handshake **nonce** delivered with the transferred MessagePort,\n * - strict `targetOrigin` on every `postMessage` (never `\"*\"`),\n * - Zod schema validation of every inbound envelope (unknown shapes dropped),\n * - nonce validation on every inbound port message (defence-in-depth on top of\n * the private MessageChannel + per-plugin origin boundary),\n * - a rolling per-second inbound rate limit.\n *\n * MCP request handling (bounded concurrency, token brokering, reply shape) is\n * structurally identical to the worker transport.\n */\nimport { z } from \"zod\";\nimport {\n type McpHttpClient,\n DEFAULT_MAX_CONCURRENT_MCP_REQUESTS,\n} from \"../worker/transport\";\n\nexport const IFRAME_BRIDGE_PROTOCOL = \"ethisys.iframe.bridge.v1\";\n\n/** Construction options for {@link IframeBridgeTransport}. */\nexport interface IframeBridgeTransportOptions {\n /** The cross-origin iframe's `contentWindow` (host never touches its document). */\n frameWindow: Window;\n /** EXACT plugin origin; every postMessage uses this as `targetOrigin`, never `\"*\"`. */\n targetOrigin: string;\n /**\n * Mints (or returns a cached) capability token. Resolved per outbound MCP HTTP\n * call on the HOST side only — the token NEVER crosses to the plugin frame.\n */\n capabilityToken: () => Promise<string>;\n /** Host-side MCP HTTP client; carries the capability token on the outbound call. */\n mcpClient: McpHttpClient;\n /** Bounded in-flight MCP requests before back-pressure. Default 8. */\n maxConcurrentMcpRequests?: number;\n /** Max inbound port messages per rolling second before drop. Default 64. */\n maxMessagesPerSecond?: number;\n /** Optional sink for dropped-message security telemetry (bad schema / nonce / rate). */\n onSecurityEvent?: (event: IframeBridgeSecurityEvent) => void;\n}\n\n/** Reason an inbound message was dropped. */\nexport type IframeBridgeSecurityEvent =\n | { reason: \"schema-invalid\" }\n | { reason: \"nonce-mismatch\" }\n | { reason: \"rate-limit-exceeded\"; type: string };\n\n/** Handshake payload posted to the frame (carries the nonce + transferred port). */\nexport interface IframeHandshakePayload {\n readonly type: \"ethisys:iframe:handshake\";\n readonly protocol: string;\n readonly nonce: string;\n}\n\n// Inbound envelope schema — validated BEFORE any handling. Unknown shapes dropped.\nconst InboundEnvelope = z.discriminatedUnion(\"type\", [\n z.object({ type: z.literal(\"ethisys:mcp:invokeTool\"), id: z.string().min(1), name: z.string().min(1), args: z.unknown(), nonce: z.string() }),\n z.object({ type: z.literal(\"ethisys:mcp:getResource\"), id: z.string().min(1), uri: z.string().min(1), nonce: z.string() }),\n z.object({ type: z.literal(\"ethisys:event\"), name: z.string().min(1), payload: z.unknown(), nonce: z.string() }),\n]);\n\ntype InboundMessage = z.infer<typeof InboundEnvelope>;\n\n/** base64url-encode raw bytes (no padding). 32 bytes → 43 chars. */\nfunction base64url(bytes: Uint8Array): string {\n let binary = \"\";\n for (const b of bytes) {\n binary += String.fromCharCode(b);\n }\n return btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\nfunction mintNonce(): string {\n const bytes = new Uint8Array(32);\n crypto.getRandomValues(bytes);\n return base64url(bytes);\n}\n\n/**\n * Host-side cross-origin iframe bridge. Construct, then call {@link connect} to\n * post the handshake (transferring one end of a fresh `MessageChannel`). The\n * plugin frame must echo the handshake `nonce` on every port message.\n */\nexport class IframeBridgeTransport {\n private readonly frameWindowRef: { current: Window | null };\n private readonly targetOrigin: string;\n private readonly capabilityTokenProvider: () => Promise<string>;\n private readonly mcpClient: McpHttpClient;\n private readonly maxConcurrentMcpRequests: number;\n private readonly maxMessagesPerSecond: number;\n private readonly onSecurityEvent: ((event: IframeBridgeSecurityEvent) => void) | undefined;\n private readonly abortController = new AbortController();\n\n private hostPort: MessagePort | undefined;\n private framePort: MessagePort | undefined;\n private nonce: string | undefined;\n private eventConsumer: ((name: string, payload: unknown) => void) | undefined;\n private inFlightMcpRequests = 0;\n private windowTimestamps: number[] = [];\n private connected = false;\n private disposed = false;\n\n public constructor(options: IframeBridgeTransportOptions) {\n this.frameWindowRef = { current: options.frameWindow };\n this.targetOrigin = options.targetOrigin;\n this.capabilityTokenProvider = options.capabilityToken;\n this.mcpClient = options.mcpClient;\n this.maxConcurrentMcpRequests = Math.max(1, options.maxConcurrentMcpRequests ?? DEFAULT_MAX_CONCURRENT_MCP_REQUESTS);\n this.maxMessagesPerSecond = Math.max(1, options.maxMessagesPerSecond ?? 64);\n this.onSecurityEvent = options.onSecurityEvent;\n }\n\n /**\n * Post the handshake to the frame. Idempotent — only the FIRST call mints the\n * nonce, creates the channel, wires the host port, and transfers the frame\n * port. The handshake uses the exact `targetOrigin` (never `\"*\"`).\n */\n public connect(): void {\n if (this.connected || this.disposed) {\n return;\n }\n this.connected = true;\n\n this.nonce = mintNonce();\n const channel = new MessageChannel();\n this.hostPort = channel.port1;\n this.framePort = channel.port2;\n this.hostPort.onmessage = (ev) => this.handlePortMessage(ev.data);\n this.hostPort.start();\n\n const handshake: IframeHandshakePayload = {\n type: \"ethisys:iframe:handshake\",\n protocol: IFRAME_BRIDGE_PROTOCOL,\n nonce: this.nonce,\n };\n const frameWindow = this.frameWindowRef.current;\n frameWindow?.postMessage(handshake, this.targetOrigin, [this.framePort]);\n }\n\n /**\n * The per-mount handshake nonce (set after {@link connect}, `undefined` before\n * connect / after dispose). The host mount reads this to validate inbound\n * cross-origin `window`-level messages (which carry no MessagePort identity).\n */\n public get connectedNonce(): string | undefined {\n return this.nonce;\n }\n\n /** Register a consumer for inbound plugin→host `ethisys:event` envelopes. */\n public onEvent(consumer: (name: string, payload: unknown) => void): void {\n this.eventConsumer = consumer;\n }\n\n /**\n * Push a host→plugin envelope (theme/nav/chrome). Always uses the exact\n * `targetOrigin` (never `\"*\"`) and stamps the handshake nonce.\n */\n public postOutbound(envelope: Record<string, unknown>): void {\n if (this.disposed || this.nonce === undefined) {\n return;\n }\n const frameWindow = this.frameWindowRef.current;\n frameWindow?.postMessage({ ...envelope, nonce: this.nonce }, this.targetOrigin);\n }\n\n /** Tear down. Idempotent. Does NOT remove the iframe (the host mount owns it). */\n public dispose(): void {\n if (this.disposed) {\n return;\n }\n this.disposed = true;\n try {\n this.abortController.abort();\n } catch {\n // best-effort\n }\n try {\n this.hostPort?.close();\n } catch {\n // ignore — may already be closed\n }\n this.nonce = undefined;\n this.frameWindowRef.current = null;\n }\n\n // ─── Inbound port message handling ────────────────────────────────────────\n\n private handlePortMessage(data: unknown): void {\n if (this.disposed) {\n return;\n }\n\n // 1. Schema validation — unknown shapes are dropped.\n const parsed = InboundEnvelope.safeParse(data);\n if (!parsed.success) {\n this.onSecurityEvent?.({ reason: \"schema-invalid\" });\n return;\n }\n const message = parsed.data;\n\n // 2. Nonce validation — defence-in-depth over the private MessageChannel.\n if (message.nonce !== this.nonce) {\n this.onSecurityEvent?.({ reason: \"nonce-mismatch\" });\n return;\n }\n\n // 3. Rolling per-second rate limit.\n if (this.isRateLimited()) {\n this.onSecurityEvent?.({ reason: \"rate-limit-exceeded\", type: message.type });\n if (message.type === \"ethisys:mcp:invokeTool\" || message.type === \"ethisys:mcp:getResource\") {\n this.safePostMessage({\n id: message.id,\n type: `${message.type}:result`,\n ok: false,\n error: \"iframe-bridge rate limit exceeded\",\n });\n }\n return;\n }\n\n // 4. Route.\n switch (message.type) {\n case \"ethisys:mcp:invokeTool\":\n this.dispatchMcp(message, \"ethisys:mcp:invokeTool:result\", (m) => this.handleInvokeTool(m));\n return;\n case \"ethisys:mcp:getResource\":\n this.dispatchMcp(message, \"ethisys:mcp:getResource:result\", (m) => this.handleGetResource(m));\n return;\n case \"ethisys:event\":\n this.eventConsumer?.(message.name, message.payload);\n return;\n }\n }\n\n private isRateLimited(): boolean {\n const now = Date.now();\n const cutoff = now - 1000;\n this.windowTimestamps = this.windowTimestamps.filter((t) => t > cutoff);\n if (this.windowTimestamps.length >= this.maxMessagesPerSecond) {\n return true;\n }\n this.windowTimestamps.push(now);\n return false;\n }\n\n // ─── MCP brokering (structurally identical to the worker transport) ───────\n\n private dispatchMcp<T extends { id: string }>(\n message: T,\n resultType: string,\n handler: (m: T) => Promise<void>,\n ): void {\n if (this.inFlightMcpRequests >= this.maxConcurrentMcpRequests) {\n this.safePostMessage({\n id: message.id,\n type: resultType,\n ok: false,\n error: `MCP back-pressure: in-flight cap of ${this.maxConcurrentMcpRequests} reached.`,\n });\n return;\n }\n this.inFlightMcpRequests++;\n handler(message).finally(() => {\n this.inFlightMcpRequests = Math.max(0, this.inFlightMcpRequests - 1);\n });\n }\n\n private async handleInvokeTool(message: Extract<InboundMessage, { type: \"ethisys:mcp:invokeTool\" }>): Promise<void> {\n let token: string;\n try {\n token = await this.capabilityTokenProvider();\n } catch (err) {\n this.replyError(message.id, \"ethisys:mcp:invokeTool:result\", err);\n return;\n }\n if (this.disposed) {\n return;\n }\n try {\n const result = await this.mcpClient.fetch({\n kind: \"invokeTool\",\n name: message.name,\n args: message.args,\n capabilityToken: token,\n signal: this.abortController.signal,\n });\n // CRITICAL: the response payload must never include the token.\n this.safePostMessage({\n id: message.id,\n type: \"ethisys:mcp:invokeTool:result\",\n ok: result.ok,\n data: result.data,\n error: result.error,\n });\n } catch (err) {\n this.replyError(message.id, \"ethisys:mcp:invokeTool:result\", err);\n }\n }\n\n private async handleGetResource(message: Extract<InboundMessage, { type: \"ethisys:mcp:getResource\" }>): Promise<void> {\n let token: string;\n try {\n token = await this.capabilityTokenProvider();\n } catch (err) {\n this.replyError(message.id, \"ethisys:mcp:getResource:result\", err);\n return;\n }\n if (this.disposed) {\n return;\n }\n try {\n const result = await this.mcpClient.fetch({\n kind: \"getResource\",\n uri: message.uri,\n capabilityToken: token,\n signal: this.abortController.signal,\n });\n this.safePostMessage({\n id: message.id,\n type: \"ethisys:mcp:getResource:result\",\n ok: result.ok,\n data: result.data,\n error: result.error,\n });\n } catch (err) {\n this.replyError(message.id, \"ethisys:mcp:getResource:result\", err);\n }\n }\n\n private replyError(id: string, type: string, err: unknown): void {\n const message = err instanceof Error ? err.message : \"MCP request failed\";\n this.safePostMessage({ id, type, ok: false, error: message });\n }\n\n /** Best-effort reply over the host port; tolerates post-dispose races. */\n private safePostMessage(message: unknown): void {\n if (this.disposed || this.hostPort === undefined) {\n return;\n }\n try {\n this.hostPort.postMessage(message);\n } catch {\n // Port may have closed between the disposed check and the post.\n }\n }\n}\n"]}
@@ -54,6 +54,16 @@ function createOffscreenCanvasTransfer(options) {
54
54
  return { offscreen };
55
55
  }
56
56
 
57
+ // src/host/worker/bridge-envelopes.ts
58
+ var BRIDGE_PUSH_THEME = "ethisys:bridge:theme";
59
+ var BRIDGE_PUSH_LOCALE = "ethisys:bridge:locale";
60
+ var BRIDGE_PUSH_DENSITY = "ethisys:bridge:density";
61
+ var BRIDGE_PUSH_A11Y = "ethisys:bridge:a11y";
62
+ var BRIDGE_NAV_PUSH = "ethisys:bridge:nav";
63
+ var BRIDGE_CHROME_REQUEST = "ethisys:bridge:chrome:request";
64
+ var BRIDGE_LIFECYCLE_EVENT = "ethisys:bridge:lifecycle";
65
+ var BRIDGE_A11Y_ANNOUNCE = "ethisys:bridge:a11y:announce";
66
+
57
67
  // src/host/worker/transport.ts
58
68
  var WORKER_TRANSPORT_PROTOCOL = "ethisys.worker.remotedom.v1";
59
69
  var DEFAULT_MAX_CONCURRENT_MCP_REQUESTS = 8;
@@ -68,6 +78,7 @@ var WorkerRemoteDomTransport = class {
68
78
  abortController;
69
79
  inFlightMcpRequests = 0;
70
80
  remoteDomConsumer;
81
+ bridgeMessageConsumer;
71
82
  disposed = false;
72
83
  connected = false;
73
84
  constructor(options) {
@@ -204,6 +215,39 @@ var WorkerRemoteDomTransport = class {
204
215
  }
205
216
  };
206
217
  }
218
+ // ─── Bridge push methods (WI 4858 sub-plan 2) ─────────────────────────────
219
+ /**
220
+ * Register a consumer for inbound plugin→host bridge messages
221
+ * (chrome requests, a11y announce, lifecycle events). Only the most recently
222
+ * registered consumer is kept — the host mount wires exactly one.
223
+ */
224
+ onBridgeMessage(consumer) {
225
+ this.bridgeMessageConsumer = consumer;
226
+ }
227
+ /**
228
+ * Push the current host theme to the plugin worker. Safe to call on every
229
+ * host theme-context change — the transport coalesces nothing here; rate
230
+ * limiting at the call site is the host's responsibility.
231
+ */
232
+ pushTheme(payload) {
233
+ this.safePostMessage({ type: BRIDGE_PUSH_THEME, ...payload });
234
+ }
235
+ /** Push the current host locale and text direction to the plugin worker. */
236
+ pushLocale(payload) {
237
+ this.safePostMessage({ type: BRIDGE_PUSH_LOCALE, ...payload });
238
+ }
239
+ /** Push the current UI density preference to the plugin worker. */
240
+ pushDensity(payload) {
241
+ this.safePostMessage({ type: BRIDGE_PUSH_DENSITY, ...payload });
242
+ }
243
+ /** Push updated accessibility preferences to the plugin worker. */
244
+ pushA11y(payload) {
245
+ this.safePostMessage({ type: BRIDGE_PUSH_A11Y, ...payload });
246
+ }
247
+ /** Push the current SPA navigation state to the plugin worker. */
248
+ pushNav(payload) {
249
+ this.safePostMessage({ type: BRIDGE_NAV_PUSH, ...payload });
250
+ }
207
251
  /**
208
252
  * Tear down the worker and port. Idempotent.
209
253
  *
@@ -260,6 +304,11 @@ var WorkerRemoteDomTransport = class {
260
304
  case "ethisys:remotedom":
261
305
  this.remoteDomConsumer?.(message.payload);
262
306
  return;
307
+ case BRIDGE_CHROME_REQUEST:
308
+ case BRIDGE_A11Y_ANNOUNCE:
309
+ case BRIDGE_LIFECYCLE_EVENT:
310
+ this.bridgeMessageConsumer?.(message);
311
+ return;
263
312
  default:
264
313
  return;
265
314
  }