@mostra/protocol 0.0.0-placeholder → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +37 -1
- package/dist/actions.d.ts +273 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +154 -0
- package/dist/actions.js.map +1 -0
- package/dist/artifact.d.ts +113 -0
- package/dist/artifact.d.ts.map +1 -0
- package/dist/artifact.js +79 -0
- package/dist/artifact.js.map +1 -0
- package/dist/bridge.d.ts +295 -0
- package/dist/bridge.d.ts.map +1 -0
- package/dist/bridge.js +343 -0
- package/dist/bridge.js.map +1 -0
- package/dist/browser.d.ts +195 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +156 -0
- package/dist/browser.js.map +1 -0
- package/dist/bundle.d.ts +155 -0
- package/dist/bundle.d.ts.map +1 -0
- package/dist/bundle.js +130 -0
- package/dist/bundle.js.map +1 -0
- package/dist/canonical.d.ts +23 -0
- package/dist/canonical.d.ts.map +1 -0
- package/dist/canonical.js +45 -0
- package/dist/canonical.js.map +1 -0
- package/dist/capability.d.ts +38 -0
- package/dist/capability.d.ts.map +1 -0
- package/dist/capability.js +61 -0
- package/dist/capability.js.map +1 -0
- package/dist/diagnostics/codes/a11y.d.ts +8 -0
- package/dist/diagnostics/codes/a11y.d.ts.map +1 -0
- package/dist/diagnostics/codes/a11y.js +30 -0
- package/dist/diagnostics/codes/a11y.js.map +1 -0
- package/dist/diagnostics/codes/action.d.ts +8 -0
- package/dist/diagnostics/codes/action.d.ts.map +1 -0
- package/dist/diagnostics/codes/action.js +110 -0
- package/dist/diagnostics/codes/action.js.map +1 -0
- package/dist/diagnostics/codes/artifact.d.ts +8 -0
- package/dist/diagnostics/codes/artifact.d.ts.map +1 -0
- package/dist/diagnostics/codes/artifact.js +121 -0
- package/dist/diagnostics/codes/artifact.js.map +1 -0
- package/dist/diagnostics/codes/build.d.ts +8 -0
- package/dist/diagnostics/codes/build.d.ts.map +1 -0
- package/dist/diagnostics/codes/build.js +55 -0
- package/dist/diagnostics/codes/build.js.map +1 -0
- package/dist/diagnostics/codes/cli.d.ts +8 -0
- package/dist/diagnostics/codes/cli.d.ts.map +1 -0
- package/dist/diagnostics/codes/cli.js +44 -0
- package/dist/diagnostics/codes/cli.js.map +1 -0
- package/dist/diagnostics/codes/console.d.ts +8 -0
- package/dist/diagnostics/codes/console.d.ts.map +1 -0
- package/dist/diagnostics/codes/console.js +30 -0
- package/dist/diagnostics/codes/console.js.map +1 -0
- package/dist/diagnostics/codes/dep.d.ts +8 -0
- package/dist/diagnostics/codes/dep.d.ts.map +1 -0
- package/dist/diagnostics/codes/dep.js +88 -0
- package/dist/diagnostics/codes/dep.js.map +1 -0
- package/dist/diagnostics/codes/env.d.ts +8 -0
- package/dist/diagnostics/codes/env.d.ts.map +1 -0
- package/dist/diagnostics/codes/env.js +132 -0
- package/dist/diagnostics/codes/env.js.map +1 -0
- package/dist/diagnostics/codes/field.d.ts +8 -0
- package/dist/diagnostics/codes/field.d.ts.map +1 -0
- package/dist/diagnostics/codes/field.js +99 -0
- package/dist/diagnostics/codes/field.js.map +1 -0
- package/dist/diagnostics/codes/home.d.ts +8 -0
- package/dist/diagnostics/codes/home.d.ts.map +1 -0
- package/dist/diagnostics/codes/home.js +132 -0
- package/dist/diagnostics/codes/home.js.map +1 -0
- package/dist/diagnostics/codes/host.d.ts +8 -0
- package/dist/diagnostics/codes/host.d.ts.map +1 -0
- package/dist/diagnostics/codes/host.js +176 -0
- package/dist/diagnostics/codes/host.js.map +1 -0
- package/dist/diagnostics/codes/hygiene.d.ts +8 -0
- package/dist/diagnostics/codes/hygiene.d.ts.map +1 -0
- package/dist/diagnostics/codes/hygiene.js +154 -0
- package/dist/diagnostics/codes/hygiene.js.map +1 -0
- package/dist/diagnostics/codes/index.d.ts +51 -0
- package/dist/diagnostics/codes/index.d.ts.map +1 -0
- package/dist/diagnostics/codes/index.js +72 -0
- package/dist/diagnostics/codes/index.js.map +1 -0
- package/dist/diagnostics/codes/live.d.ts +8 -0
- package/dist/diagnostics/codes/live.d.ts.map +1 -0
- package/dist/diagnostics/codes/live.js +30 -0
- package/dist/diagnostics/codes/live.js.map +1 -0
- package/dist/diagnostics/codes/manifest.d.ts +8 -0
- package/dist/diagnostics/codes/manifest.d.ts.map +1 -0
- package/dist/diagnostics/codes/manifest.js +44 -0
- package/dist/diagnostics/codes/manifest.js.map +1 -0
- package/dist/diagnostics/codes/network.d.ts +8 -0
- package/dist/diagnostics/codes/network.d.ts.map +1 -0
- package/dist/diagnostics/codes/network.js +30 -0
- package/dist/diagnostics/codes/network.js.map +1 -0
- package/dist/diagnostics/codes/platform.d.ts +8 -0
- package/dist/diagnostics/codes/platform.d.ts.map +1 -0
- package/dist/diagnostics/codes/platform.js +18 -0
- package/dist/diagnostics/codes/platform.js.map +1 -0
- package/dist/diagnostics/codes/publish.d.ts +8 -0
- package/dist/diagnostics/codes/publish.d.ts.map +1 -0
- package/dist/diagnostics/codes/publish.js +44 -0
- package/dist/diagnostics/codes/publish.js.map +1 -0
- package/dist/diagnostics/codes/regression.d.ts +8 -0
- package/dist/diagnostics/codes/regression.d.ts.map +1 -0
- package/dist/diagnostics/codes/regression.js +77 -0
- package/dist/diagnostics/codes/regression.js.map +1 -0
- package/dist/diagnostics/codes/runtime.d.ts +8 -0
- package/dist/diagnostics/codes/runtime.d.ts.map +1 -0
- package/dist/diagnostics/codes/runtime.js +66 -0
- package/dist/diagnostics/codes/runtime.js.map +1 -0
- package/dist/diagnostics/codes/secret.d.ts +8 -0
- package/dist/diagnostics/codes/secret.d.ts.map +1 -0
- package/dist/diagnostics/codes/secret.js +18 -0
- package/dist/diagnostics/codes/secret.js.map +1 -0
- package/dist/diagnostics/codes/smoke.d.ts +8 -0
- package/dist/diagnostics/codes/smoke.d.ts.map +1 -0
- package/dist/diagnostics/codes/smoke.js +30 -0
- package/dist/diagnostics/codes/smoke.js.map +1 -0
- package/dist/diagnostics/codes/submit.d.ts +8 -0
- package/dist/diagnostics/codes/submit.d.ts.map +1 -0
- package/dist/diagnostics/codes/submit.js +99 -0
- package/dist/diagnostics/codes/submit.js.map +1 -0
- package/dist/diagnostics/codes/target.d.ts +8 -0
- package/dist/diagnostics/codes/target.d.ts.map +1 -0
- package/dist/diagnostics/codes/target.js +18 -0
- package/dist/diagnostics/codes/target.js.map +1 -0
- package/dist/diagnostics/codes/type.d.ts +8 -0
- package/dist/diagnostics/codes/type.d.ts.map +1 -0
- package/dist/diagnostics/codes/type.js +30 -0
- package/dist/diagnostics/codes/type.js.map +1 -0
- package/dist/diagnostics/codes/verify.d.ts +8 -0
- package/dist/diagnostics/codes/verify.d.ts.map +1 -0
- package/dist/diagnostics/codes/verify.js +99 -0
- package/dist/diagnostics/codes/verify.js.map +1 -0
- package/dist/diagnostics/diagnostic.d.ts +79 -0
- package/dist/diagnostics/diagnostic.d.ts.map +1 -0
- package/dist/diagnostics/diagnostic.js +131 -0
- package/dist/diagnostics/diagnostic.js.map +1 -0
- package/dist/diagnostics/spec.d.ts +49 -0
- package/dist/diagnostics/spec.d.ts.map +1 -0
- package/dist/diagnostics/spec.js +31 -0
- package/dist/diagnostics/spec.js.map +1 -0
- package/dist/hygiene.d.ts +66 -0
- package/dist/hygiene.d.ts.map +1 -0
- package/dist/hygiene.js +134 -0
- package/dist/hygiene.js.map +1 -0
- package/dist/ids.d.ts +119 -0
- package/dist/ids.d.ts.map +1 -0
- package/dist/ids.js +135 -0
- package/dist/ids.js.map +1 -0
- package/dist/index.d.ts +86 -0
- package/dist/index.js +86 -0
- package/dist/interaction.d.ts +391 -0
- package/dist/interaction.d.ts.map +1 -0
- package/dist/interaction.js +306 -0
- package/dist/interaction.js.map +1 -0
- package/dist/manifest.d.ts +97 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +101 -0
- package/dist/manifest.js.map +1 -0
- package/dist/method.d.ts +26 -0
- package/dist/method.d.ts.map +1 -0
- package/dist/method.js +10 -0
- package/dist/method.js.map +1 -0
- package/dist/methods/artifact/create.d.ts +29 -0
- package/dist/methods/artifact/create.d.ts.map +1 -0
- package/dist/methods/artifact/create.js +31 -0
- package/dist/methods/artifact/create.js.map +1 -0
- package/dist/methods/artifact/get.d.ts +17 -0
- package/dist/methods/artifact/get.d.ts.map +1 -0
- package/dist/methods/artifact/get.js +21 -0
- package/dist/methods/artifact/get.js.map +1 -0
- package/dist/methods/artifact/list.d.ts +22 -0
- package/dist/methods/artifact/list.d.ts.map +1 -0
- package/dist/methods/artifact/list.js +19 -0
- package/dist/methods/artifact/list.js.map +1 -0
- package/dist/methods/artifact/publish.d.ts +28 -0
- package/dist/methods/artifact/publish.d.ts.map +1 -0
- package/dist/methods/artifact/publish.js +28 -0
- package/dist/methods/artifact/publish.js.map +1 -0
- package/dist/methods/artifact/verify.d.ts +29 -0
- package/dist/methods/artifact/verify.d.ts.map +1 -0
- package/dist/methods/artifact/verify.js +27 -0
- package/dist/methods/artifact/verify.js.map +1 -0
- package/dist/methods/deps/add.d.ts +43 -0
- package/dist/methods/deps/add.d.ts.map +1 -0
- package/dist/methods/deps/add.js +40 -0
- package/dist/methods/deps/add.js.map +1 -0
- package/dist/methods/dev/start.d.ts +52 -0
- package/dist/methods/dev/start.d.ts.map +1 -0
- package/dist/methods/dev/start.js +56 -0
- package/dist/methods/dev/start.js.map +1 -0
- package/dist/methods/dev/stop.d.ts +16 -0
- package/dist/methods/dev/stop.d.ts.map +1 -0
- package/dist/methods/dev/stop.js +21 -0
- package/dist/methods/dev/stop.js.map +1 -0
- package/dist/methods/events/ack.d.ts +20 -0
- package/dist/methods/events/ack.d.ts.map +1 -0
- package/dist/methods/events/ack.js +26 -0
- package/dist/methods/events/ack.js.map +1 -0
- package/dist/methods/events/read.d.ts +40 -0
- package/dist/methods/events/read.d.ts.map +1 -0
- package/dist/methods/events/read.js +35 -0
- package/dist/methods/events/read.js.map +1 -0
- package/dist/methods/events/subscribe.d.ts +54 -0
- package/dist/methods/events/subscribe.d.ts.map +1 -0
- package/dist/methods/events/subscribe.js +44 -0
- package/dist/methods/events/subscribe.js.map +1 -0
- package/dist/methods/events/unsubscribe.d.ts +16 -0
- package/dist/methods/events/unsubscribe.d.ts.map +1 -0
- package/dist/methods/events/unsubscribe.js +21 -0
- package/dist/methods/events/unsubscribe.js.map +1 -0
- package/dist/methods/gc/run.d.ts +76 -0
- package/dist/methods/gc/run.d.ts.map +1 -0
- package/dist/methods/gc/run.js +56 -0
- package/dist/methods/gc/run.js.map +1 -0
- package/dist/methods/index.d.ts +6 -0
- package/dist/methods/index.d.ts.map +1 -0
- package/dist/methods/index.js +78 -0
- package/dist/methods/index.js.map +1 -0
- package/dist/methods/instance/check.d.ts +30 -0
- package/dist/methods/instance/check.d.ts.map +1 -0
- package/dist/methods/instance/check.js +34 -0
- package/dist/methods/instance/check.js.map +1 -0
- package/dist/methods/instance/status.d.ts +102 -0
- package/dist/methods/instance/status.d.ts.map +1 -0
- package/dist/methods/instance/status.js +83 -0
- package/dist/methods/instance/status.js.map +1 -0
- package/dist/methods/live/push.d.ts +28 -0
- package/dist/methods/live/push.d.ts.map +1 -0
- package/dist/methods/live/push.js +35 -0
- package/dist/methods/live/push.js.map +1 -0
- package/dist/methods/mostra/initialize.d.ts +143 -0
- package/dist/methods/mostra/initialize.d.ts.map +1 -0
- package/dist/methods/mostra/initialize.js +97 -0
- package/dist/methods/mostra/initialize.js.map +1 -0
- package/dist/methods/mostra/shutdown.d.ts +14 -0
- package/dist/methods/mostra/shutdown.d.ts.map +1 -0
- package/dist/methods/mostra/shutdown.js +22 -0
- package/dist/methods/mostra/shutdown.js.map +1 -0
- package/dist/methods/port/drafts-mirror.d.ts +30 -0
- package/dist/methods/port/drafts-mirror.d.ts.map +1 -0
- package/dist/methods/port/drafts-mirror.js +33 -0
- package/dist/methods/port/drafts-mirror.js.map +1 -0
- package/dist/methods/port/secrets-get.d.ts +19 -0
- package/dist/methods/port/secrets-get.d.ts.map +1 -0
- package/dist/methods/port/secrets-get.js +31 -0
- package/dist/methods/port/secrets-get.js.map +1 -0
- package/dist/methods/port/submissions-deliver.d.ts +38 -0
- package/dist/methods/port/submissions-deliver.d.ts.map +1 -0
- package/dist/methods/port/submissions-deliver.js +38 -0
- package/dist/methods/port/submissions-deliver.js.map +1 -0
- package/dist/methods/port/trusted-controls-activated.d.ts +27 -0
- package/dist/methods/port/trusted-controls-activated.d.ts.map +1 -0
- package/dist/methods/port/trusted-controls-activated.js +33 -0
- package/dist/methods/port/trusted-controls-activated.js.map +1 -0
- package/dist/methods/port/trusted-controls-policy.d.ts +21 -0
- package/dist/methods/port/trusted-controls-policy.d.ts.map +1 -0
- package/dist/methods/port/trusted-controls-policy.js +27 -0
- package/dist/methods/port/trusted-controls-policy.js.map +1 -0
- package/dist/methods/results/get.d.ts +32 -0
- package/dist/methods/results/get.d.ts.map +1 -0
- package/dist/methods/results/get.js +36 -0
- package/dist/methods/results/get.js.map +1 -0
- package/dist/methods/secret/delete.d.ts +22 -0
- package/dist/methods/secret/delete.d.ts.map +1 -0
- package/dist/methods/secret/delete.js +32 -0
- package/dist/methods/secret/delete.js.map +1 -0
- package/dist/methods/secret/list.d.ts +21 -0
- package/dist/methods/secret/list.d.ts.map +1 -0
- package/dist/methods/secret/list.js +29 -0
- package/dist/methods/secret/list.js.map +1 -0
- package/dist/methods/secret/set.d.ts +37 -0
- package/dist/methods/secret/set.d.ts.map +1 -0
- package/dist/methods/secret/set.js +41 -0
- package/dist/methods/secret/set.js.map +1 -0
- package/dist/methods/state/get.d.ts +25 -0
- package/dist/methods/state/get.d.ts.map +1 -0
- package/dist/methods/state/get.js +29 -0
- package/dist/methods/state/get.js.map +1 -0
- package/dist/methods/state/patch.d.ts +32 -0
- package/dist/methods/state/patch.d.ts.map +1 -0
- package/dist/methods/state/patch.js +29 -0
- package/dist/methods/state/patch.js.map +1 -0
- package/dist/methods/view/default.d.ts +19 -0
- package/dist/methods/view/default.d.ts.map +1 -0
- package/dist/methods/view/default.js +23 -0
- package/dist/methods/view/default.js.map +1 -0
- package/dist/methods/view/launch.d.ts +27 -0
- package/dist/methods/view/launch.d.ts.map +1 -0
- package/dist/methods/view/launch.js +30 -0
- package/dist/methods/view/launch.js.map +1 -0
- package/dist/methods/view/revoke.d.ts +19 -0
- package/dist/methods/view/revoke.d.ts.map +1 -0
- package/dist/methods/view/revoke.js +26 -0
- package/dist/methods/view/revoke.js.map +1 -0
- package/dist/methods/view/rotate.d.ts +21 -0
- package/dist/methods/view/rotate.d.ts.map +1 -0
- package/dist/methods/view/rotate.js +27 -0
- package/dist/methods/view/rotate.js.map +1 -0
- package/dist/methods/view/url.d.ts +52 -0
- package/dist/methods/view/url.d.ts.map +1 -0
- package/dist/methods/view/url.js +43 -0
- package/dist/methods/view/url.js.map +1 -0
- package/dist/methods/wait/wait.d.ts +37 -0
- package/dist/methods/wait/wait.d.ts.map +1 -0
- package/dist/methods/wait/wait.js +39 -0
- package/dist/methods/wait/wait.js.map +1 -0
- package/dist/revision.d.ts +43 -0
- package/dist/revision.d.ts.map +1 -0
- package/dist/revision.js +69 -0
- package/dist/revision.js.map +1 -0
- package/dist/rpc/error.d.ts +47 -0
- package/dist/rpc/error.d.ts.map +1 -0
- package/dist/rpc/error.js +77 -0
- package/dist/rpc/error.js.map +1 -0
- package/dist/rpc/lines.d.ts +30 -0
- package/dist/rpc/lines.d.ts.map +1 -0
- package/dist/rpc/lines.js +63 -0
- package/dist/rpc/lines.js.map +1 -0
- package/dist/rpc/messages.d.ts +92 -0
- package/dist/rpc/messages.d.ts.map +1 -0
- package/dist/rpc/messages.js +133 -0
- package/dist/rpc/messages.js.map +1 -0
- package/dist/rpc/peer.d.ts +81 -0
- package/dist/rpc/peer.d.ts.map +1 -0
- package/dist/rpc/peer.js +208 -0
- package/dist/rpc/peer.js.map +1 -0
- package/dist/serializer.d.ts +178 -0
- package/dist/serializer.d.ts.map +1 -0
- package/dist/serializer.js +929 -0
- package/dist/serializer.js.map +1 -0
- package/dist/verify.d.ts +254 -0
- package/dist/verify.d.ts.map +1 -0
- package/dist/verify.js +243 -0
- package/dist/verify.js.map +1 -0
- package/dist/view.d.ts +103 -0
- package/dist/view.d.ts.map +1 -0
- package/dist/view.js +110 -0
- package/dist/view.js.map +1 -0
- package/dist/wire-registry.js +11 -0
- package/dist/wire-registry.js.map +1 -0
- package/dist/worker.d.ts +249 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +158 -0
- package/dist/worker.js.map +1 -0
- package/package.json +114 -4
- package/schemas/protocol.draft-07.json +5120 -0
- package/schemas/protocol.schema.json +4964 -0
- package/src/actions.ts +418 -0
- package/src/artifact.ts +187 -0
- package/src/bridge.ts +618 -0
- package/src/browser.ts +374 -0
- package/src/bundle.ts +264 -0
- package/src/canonical.ts +49 -0
- package/src/capability.ts +92 -0
- package/src/diagnostics/codes/a11y.ts +41 -0
- package/src/diagnostics/codes/action.ts +125 -0
- package/src/diagnostics/codes/artifact.ts +137 -0
- package/src/diagnostics/codes/build.ts +65 -0
- package/src/diagnostics/codes/cli.ts +53 -0
- package/src/diagnostics/codes/console.ts +41 -0
- package/src/diagnostics/codes/dep.ts +101 -0
- package/src/diagnostics/codes/env.ts +149 -0
- package/src/diagnostics/codes/field.ts +113 -0
- package/src/diagnostics/codes/home.ts +150 -0
- package/src/diagnostics/codes/host.ts +197 -0
- package/src/diagnostics/codes/hygiene.ts +175 -0
- package/src/diagnostics/codes/index.ts +142 -0
- package/src/diagnostics/codes/live.ts +41 -0
- package/src/diagnostics/codes/manifest.ts +53 -0
- package/src/diagnostics/codes/network.ts +41 -0
- package/src/diagnostics/codes/platform.ts +30 -0
- package/src/diagnostics/codes/publish.ts +53 -0
- package/src/diagnostics/codes/regression.ts +89 -0
- package/src/diagnostics/codes/runtime.ts +77 -0
- package/src/diagnostics/codes/secret.ts +29 -0
- package/src/diagnostics/codes/smoke.ts +41 -0
- package/src/diagnostics/codes/submit.ts +113 -0
- package/src/diagnostics/codes/target.ts +29 -0
- package/src/diagnostics/codes/type.ts +41 -0
- package/src/diagnostics/codes/verify.ts +113 -0
- package/src/diagnostics/diagnostic.ts +240 -0
- package/src/diagnostics/spec.ts +90 -0
- package/src/hygiene.ts +224 -0
- package/src/ids.ts +279 -0
- package/src/index.ts +576 -0
- package/src/interaction.ts +728 -0
- package/src/manifest.ts +206 -0
- package/src/method.ts +42 -0
- package/src/methods/artifact/create.ts +56 -0
- package/src/methods/artifact/get.ts +30 -0
- package/src/methods/artifact/list.ts +42 -0
- package/src/methods/artifact/publish.ts +46 -0
- package/src/methods/artifact/verify.ts +46 -0
- package/src/methods/deps/add.ts +72 -0
- package/src/methods/dev/start.ts +84 -0
- package/src/methods/dev/stop.ts +32 -0
- package/src/methods/events/ack.ts +41 -0
- package/src/methods/events/read.ts +67 -0
- package/src/methods/events/subscribe.ts +91 -0
- package/src/methods/events/unsubscribe.ts +35 -0
- package/src/methods/gc/run.ts +125 -0
- package/src/methods/index.ts +81 -0
- package/src/methods/instance/check.ts +61 -0
- package/src/methods/instance/status.ts +172 -0
- package/src/methods/live/push.ts +54 -0
- package/src/methods/mostra/initialize.ts +249 -0
- package/src/methods/mostra/shutdown.ts +31 -0
- package/src/methods/port/drafts-mirror.ts +52 -0
- package/src/methods/port/secrets-get.ts +40 -0
- package/src/methods/port/submissions-deliver.ts +70 -0
- package/src/methods/port/trusted-controls-activated.ts +51 -0
- package/src/methods/port/trusted-controls-policy.ts +38 -0
- package/src/methods/results/get.ts +59 -0
- package/src/methods/secret/delete.ts +43 -0
- package/src/methods/secret/list.ts +40 -0
- package/src/methods/secret/set.ts +69 -0
- package/src/methods/state/get.ts +44 -0
- package/src/methods/state/patch.ts +51 -0
- package/src/methods/view/default.ts +32 -0
- package/src/methods/view/launch.ts +47 -0
- package/src/methods/view/revoke.ts +39 -0
- package/src/methods/view/rotate.ts +37 -0
- package/src/methods/view/url.ts +84 -0
- package/src/methods/wait/wait.ts +58 -0
- package/src/revision.ts +101 -0
- package/src/rpc/error.ts +107 -0
- package/src/rpc/lines.ts +70 -0
- package/src/rpc/messages.ts +188 -0
- package/src/rpc/peer.ts +341 -0
- package/src/serializer.ts +1217 -0
- package/src/timers.d.ts +8 -0
- package/src/verify.ts +458 -0
- package/src/view.ts +200 -0
- package/src/web-crypto.d.ts +9 -0
- package/src/web-platform.d.ts +35 -0
- package/src/wire-registry.ts +14 -0
- package/src/worker.ts +374 -0
package/src/bridge.ts
ADDED
|
@@ -0,0 +1,618 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shell ↔ artifact bridge (INTERFACES §5, ROUND-3 §8, SH-04): JSON-RPC 2.0 over
|
|
3
|
+
* `postMessage`, shaped like MCP Apps so exporting an artifact as an MCP App only swaps the
|
|
4
|
+
* transport. Both ends accept a message only when it comes from the one window they talk to, at
|
|
5
|
+
* its exact origin, and only when it is a message of the bridge's set with valid params; anything
|
|
6
|
+
* else is dropped (or, for a request from the right window, answered with an error). It has no
|
|
7
|
+
* I/O of its own: the caller hands it the window objects.
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* // In the shell:
|
|
11
|
+
* const bridge = connectBridge({
|
|
12
|
+
* side: "shell",
|
|
13
|
+
* receiver: window,
|
|
14
|
+
* peer: () => frame.contentWindow,
|
|
15
|
+
* peerOrigin: "http://127.0.0.1:23457",
|
|
16
|
+
* onRequest: (message, params) => (message === uiInitialize ? initializeResult : undefined),
|
|
17
|
+
* });
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
import * as z from "zod";
|
|
21
|
+
import type { MethodDefinition } from "./method.ts";
|
|
22
|
+
import { hostDiagnostic, JSON_RPC_ERROR_CODES, RpcError } from "./rpc/error.ts";
|
|
23
|
+
import { classifyMessage } from "./rpc/messages.ts";
|
|
24
|
+
import { RpcPeer } from "./rpc/peer.ts";
|
|
25
|
+
import { REVIEW_SET_ID_PATTERN, ViewContextWire } from "./view.ts";
|
|
26
|
+
|
|
27
|
+
/** Which end of the bridge: the trusted shell, or the artifact's frame. */
|
|
28
|
+
export type BridgeSide = "shell" | "artifact";
|
|
29
|
+
|
|
30
|
+
/** One message of the bridge's set. */
|
|
31
|
+
export interface BridgeMessage<P, R> {
|
|
32
|
+
/** The JSON-RPC method name; MCP Apps' name where the concept exists. */
|
|
33
|
+
readonly method: string;
|
|
34
|
+
/** The side that sends it. */
|
|
35
|
+
readonly from: BridgeSide;
|
|
36
|
+
/** A request is answered; a notification isn't. */
|
|
37
|
+
readonly kind: "request" | "notification";
|
|
38
|
+
/** The schema of its params. */
|
|
39
|
+
readonly params: z.ZodType<P>;
|
|
40
|
+
/** The schema of a request's result (`z.undefined()` for notifications). */
|
|
41
|
+
readonly result: z.ZodType<R>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** A bridge message of any params and result type. */
|
|
45
|
+
export type AnyBridgeMessage = BridgeMessage<unknown, unknown>;
|
|
46
|
+
|
|
47
|
+
/** The version of MCP Apps whose message shapes the bridge follows (VERIFIED_FACTS, MCP). */
|
|
48
|
+
export const MCP_APPS_PROTOCOL_VERSION = "2026-01-26";
|
|
49
|
+
|
|
50
|
+
/** An application's name and version (MCP's `Implementation`). */
|
|
51
|
+
export interface BridgeImplementation {
|
|
52
|
+
/** A stable machine name, such as `mostra-shell`. */
|
|
53
|
+
readonly name: string;
|
|
54
|
+
/** Its release. */
|
|
55
|
+
readonly version: string;
|
|
56
|
+
/** A name for people. */
|
|
57
|
+
readonly title?: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const Implementation: z.ZodType<BridgeImplementation> = z.looseObject({
|
|
61
|
+
name: z.string(),
|
|
62
|
+
version: z.string(),
|
|
63
|
+
title: z.string().exactOptional(),
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
/** The params of `ui/initialize`: the artifact introduces itself (MCP Apps). */
|
|
67
|
+
export interface UiInitializeParams {
|
|
68
|
+
/** The artifact's client runtime. */
|
|
69
|
+
readonly appInfo: BridgeImplementation;
|
|
70
|
+
/** What the artifact offers (MCP Apps' `McpUiAppCapabilities`). */
|
|
71
|
+
readonly appCapabilities: Readonly<Record<string, unknown>>;
|
|
72
|
+
/** The MCP Apps revision the artifact speaks, {@link MCP_APPS_PROTOCOL_VERSION}. */
|
|
73
|
+
readonly protocolVersion: string;
|
|
74
|
+
/**
|
|
75
|
+
* What the page is (WP-07): the version whose build the frame loaded. An artifact origin serves
|
|
76
|
+
* the version its frame session last entered (D98 item 5), so a frame reloaded after another
|
|
77
|
+
* tab switched versions may hold another version than its view; the shell re-enters its own.
|
|
78
|
+
*/
|
|
79
|
+
readonly mostra?: { readonly versionId: string };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** What the shell tells the artifact about where it is shown (MCP Apps' `HostContext`). */
|
|
83
|
+
export interface BridgeHostContext {
|
|
84
|
+
/** The viewer's color scheme. */
|
|
85
|
+
readonly theme?: "light" | "dark";
|
|
86
|
+
/** How the artifact is shown: the shell shows it `fullscreen`. */
|
|
87
|
+
readonly displayMode?: "inline" | "fullscreen" | "pip";
|
|
88
|
+
/** The display modes the shell offers. */
|
|
89
|
+
readonly availableDisplayModes?: readonly string[];
|
|
90
|
+
/** The viewer's language (BCP 47). */
|
|
91
|
+
readonly locale?: string;
|
|
92
|
+
/** The viewer's time zone (IANA). */
|
|
93
|
+
readonly timeZone?: string;
|
|
94
|
+
/** The kind of device. */
|
|
95
|
+
readonly platform?: "web" | "desktop" | "mobile";
|
|
96
|
+
/** Further MCP Apps context, passed through. */
|
|
97
|
+
readonly [key: string]: unknown;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** The result of `ui/initialize`: the shell introduces itself (MCP Apps). */
|
|
101
|
+
export interface UiInitializeResult {
|
|
102
|
+
/** The MCP Apps revision both ends speak. */
|
|
103
|
+
readonly protocolVersion: string;
|
|
104
|
+
/** The shell. */
|
|
105
|
+
readonly hostInfo: BridgeImplementation;
|
|
106
|
+
/** What the shell offers (MCP Apps' `McpUiHostCapabilities`). */
|
|
107
|
+
readonly hostCapabilities: Readonly<Record<string, unknown>>;
|
|
108
|
+
/** Where and how the artifact is shown. */
|
|
109
|
+
readonly hostContext: BridgeHostContext;
|
|
110
|
+
/** Mostra's shell: the view this frame load is, and what the host allows in it (WP-07). */
|
|
111
|
+
readonly mostra?: ViewContextWire;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** `ui/initialize`: the artifact's first request, answered with the shell's context. */
|
|
115
|
+
export const uiInitialize: BridgeMessage<UiInitializeParams, UiInitializeResult> = {
|
|
116
|
+
method: "ui/initialize",
|
|
117
|
+
from: "artifact",
|
|
118
|
+
kind: "request",
|
|
119
|
+
params: z.looseObject({
|
|
120
|
+
appInfo: Implementation,
|
|
121
|
+
appCapabilities: z.record(z.string(), z.unknown()),
|
|
122
|
+
protocolVersion: z.string().min(1),
|
|
123
|
+
mostra: z.looseObject({ versionId: z.string().min(1).max(64) }).exactOptional(),
|
|
124
|
+
}),
|
|
125
|
+
result: z.looseObject({
|
|
126
|
+
protocolVersion: z.string().min(1),
|
|
127
|
+
hostInfo: Implementation,
|
|
128
|
+
hostCapabilities: z.record(z.string(), z.unknown()),
|
|
129
|
+
hostContext: z.looseObject({
|
|
130
|
+
theme: z.enum(["light", "dark"]).exactOptional(),
|
|
131
|
+
displayMode: z.enum(["inline", "fullscreen", "pip"]).exactOptional(),
|
|
132
|
+
availableDisplayModes: z.array(z.string()).readonly().exactOptional(),
|
|
133
|
+
locale: z.string().exactOptional(),
|
|
134
|
+
timeZone: z.string().exactOptional(),
|
|
135
|
+
platform: z.enum(["web", "desktop", "mobile"]).exactOptional(),
|
|
136
|
+
}),
|
|
137
|
+
mostra: ViewContextWire.exactOptional(),
|
|
138
|
+
}),
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/** `ui/notifications/initialized`: the artifact finished initializing; the shell may now send. */
|
|
142
|
+
export const uiInitialized: BridgeMessage<Readonly<Record<string, unknown>>, undefined> = {
|
|
143
|
+
method: "ui/notifications/initialized",
|
|
144
|
+
from: "artifact",
|
|
145
|
+
kind: "notification",
|
|
146
|
+
params: z.record(z.string(), z.unknown()),
|
|
147
|
+
result: z.undefined(),
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
/** `ui/notifications/size-changed`: the artifact's rendered size (MCP Apps). */
|
|
151
|
+
export const uiSizeChanged: BridgeMessage<
|
|
152
|
+
{ readonly width?: number; readonly height?: number },
|
|
153
|
+
undefined
|
|
154
|
+
> = {
|
|
155
|
+
method: "ui/notifications/size-changed",
|
|
156
|
+
from: "artifact",
|
|
157
|
+
kind: "notification",
|
|
158
|
+
params: z.looseObject({
|
|
159
|
+
width: z.number().min(0).exactOptional(),
|
|
160
|
+
height: z.number().min(0).exactOptional(),
|
|
161
|
+
}),
|
|
162
|
+
result: z.undefined(),
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* The URL schemes `ui/open-link` opens: the web, mail and phone numbers. Anything else, such as
|
|
167
|
+
* `javascript:` or `data:`, would run or show content in the shell's own context.
|
|
168
|
+
*/
|
|
169
|
+
export const OPEN_LINK_SCHEMES: readonly string[] = ["http:", "https:", "mailto:", "tel:"];
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* `ui/open-link`: the artifact asks the shell to open a URL in a new tab (MCP Apps). The frame
|
|
173
|
+
* can't navigate the viewer's tab itself (D98 item 7), so this is how an artifact sends the
|
|
174
|
+
* viewer anywhere. `isError` is `true` when the shell couldn't open it.
|
|
175
|
+
*/
|
|
176
|
+
export const uiOpenLink: BridgeMessage<
|
|
177
|
+
{ readonly url: string },
|
|
178
|
+
{ readonly isError?: boolean; readonly [key: string]: unknown }
|
|
179
|
+
> = {
|
|
180
|
+
method: "ui/open-link",
|
|
181
|
+
from: "artifact",
|
|
182
|
+
kind: "request",
|
|
183
|
+
params: z.looseObject({ url: z.string().min(1).max(8192) }),
|
|
184
|
+
result: z.looseObject({ isError: z.boolean().exactOptional() }),
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
/** `mostra/ready`: the artifact's page has rendered; `verify` waits for it (ROUND-3 §8). */
|
|
188
|
+
export const mostraReady: BridgeMessage<Readonly<Record<string, unknown>>, undefined> = {
|
|
189
|
+
method: "mostra/ready",
|
|
190
|
+
from: "artifact",
|
|
191
|
+
kind: "notification",
|
|
192
|
+
params: z.record(z.string(), z.unknown()),
|
|
193
|
+
result: z.undefined(),
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
// ── Fields, pages, controls and versions (WP-07, INTERFACES §5, ROUND-3 §8) ──────────────
|
|
197
|
+
|
|
198
|
+
/** The page's save state, as the shell's rail shows it (D47). */
|
|
199
|
+
export type SaveState = "saved" | "saving" | "offline" | "conflict";
|
|
200
|
+
|
|
201
|
+
/** Why a field blocks a submit (INTERFACES §2.3). */
|
|
202
|
+
export interface SubmitBlocker {
|
|
203
|
+
/** The field's ID. */
|
|
204
|
+
readonly fieldId: string;
|
|
205
|
+
/** Its label, when it has one. */
|
|
206
|
+
readonly label?: string;
|
|
207
|
+
/** `required` without a value, `invalid` by its `validate`, or a `conflict` not yet resolved. */
|
|
208
|
+
readonly reason: "required" | "invalid" | "conflict";
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* The longest label, validation message or other text a field carries over the bridge; a page
|
|
213
|
+
* shortens longer ones before it sends them (Claude review finding 11).
|
|
214
|
+
*/
|
|
215
|
+
export const BRIDGE_TEXT_MAX = 1024;
|
|
216
|
+
|
|
217
|
+
/** The most fields or blockers one bridge message lists; a page sends those that matter first. */
|
|
218
|
+
export const BRIDGE_FIELDS_MAX = 10_000;
|
|
219
|
+
|
|
220
|
+
const SubmitBlockerSchema: z.ZodType<SubmitBlocker> = z.looseObject({
|
|
221
|
+
fieldId: z.string().min(1).max(256),
|
|
222
|
+
label: z.string().max(BRIDGE_TEXT_MAX).exactOptional(),
|
|
223
|
+
reason: z.enum(["required", "invalid", "conflict"]),
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* A field the page has mounted, with what the shell needs to list blockers by label (D52, D106).
|
|
228
|
+
* The page reports it; the shell checks `required` against the draft it reads itself.
|
|
229
|
+
*/
|
|
230
|
+
export interface FieldRegistration {
|
|
231
|
+
/** The field's ID. */
|
|
232
|
+
readonly id: string;
|
|
233
|
+
/** The label people see. */
|
|
234
|
+
readonly label?: string;
|
|
235
|
+
/** Whether a submit needs a value. */
|
|
236
|
+
readonly required?: boolean;
|
|
237
|
+
/** The viewer's draft or the artifact's shared state. */
|
|
238
|
+
readonly scope: "viewer" | "artifact";
|
|
239
|
+
/** Whether its value is kept out of events and logs. */
|
|
240
|
+
readonly sensitive?: boolean;
|
|
241
|
+
/** Its `validate` message for the current value, if any. */
|
|
242
|
+
readonly error?: string;
|
|
243
|
+
/** Whether it has a conflict the viewer hasn't resolved. */
|
|
244
|
+
readonly conflict?: boolean;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** The params of `mostra/fields`. */
|
|
248
|
+
export interface FieldsParams {
|
|
249
|
+
/** Every mounted field, once per ID. */
|
|
250
|
+
readonly fields: readonly FieldRegistration[];
|
|
251
|
+
/** The page's save state. */
|
|
252
|
+
readonly save: SaveState;
|
|
253
|
+
/** Whether edits are waiting to be saved (typed, offline, or in the journal). */
|
|
254
|
+
readonly unsaved: boolean;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* `mostra/fields`: the page's mounted fields and save state, sent whenever they change, so the
|
|
259
|
+
* shell lists blockers by label and its rail shows "Saving" and "Saved" (INTERFACES §5).
|
|
260
|
+
*/
|
|
261
|
+
export const mostraFields: BridgeMessage<FieldsParams, undefined> = {
|
|
262
|
+
method: "mostra/fields",
|
|
263
|
+
from: "artifact",
|
|
264
|
+
kind: "notification",
|
|
265
|
+
params: z.looseObject({
|
|
266
|
+
fields: z
|
|
267
|
+
.array(
|
|
268
|
+
z.looseObject({
|
|
269
|
+
id: z.string().min(1).max(256),
|
|
270
|
+
label: z.string().max(BRIDGE_TEXT_MAX).exactOptional(),
|
|
271
|
+
required: z.boolean().exactOptional(),
|
|
272
|
+
scope: z.enum(["viewer", "artifact"]),
|
|
273
|
+
sensitive: z.boolean().exactOptional(),
|
|
274
|
+
error: z.string().max(BRIDGE_TEXT_MAX).exactOptional(),
|
|
275
|
+
conflict: z.boolean().exactOptional(),
|
|
276
|
+
}),
|
|
277
|
+
)
|
|
278
|
+
.max(BRIDGE_FIELDS_MAX)
|
|
279
|
+
.readonly(),
|
|
280
|
+
save: z.enum(["saved", "saving", "offline", "conflict"]),
|
|
281
|
+
unsaved: z.boolean(),
|
|
282
|
+
}),
|
|
283
|
+
result: z.undefined(),
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* `mostra/page`: the page the viewer is on, inside a `pages/` artifact (D57), so comments record
|
|
288
|
+
* it (WP-20). `path` is the route's path with its search and hash, whatever the history mode.
|
|
289
|
+
*/
|
|
290
|
+
export const mostraPage: BridgeMessage<
|
|
291
|
+
{ readonly path: string; readonly title?: string },
|
|
292
|
+
undefined
|
|
293
|
+
> = {
|
|
294
|
+
method: "mostra/page",
|
|
295
|
+
from: "artifact",
|
|
296
|
+
kind: "notification",
|
|
297
|
+
params: z.looseObject({
|
|
298
|
+
path: z.string().min(1).max(2048),
|
|
299
|
+
title: z.string().max(1024).exactOptional(),
|
|
300
|
+
}),
|
|
301
|
+
result: z.undefined(),
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
/** A trusted control the artifact asks the shell for (INTERFACES §5): the artifact asks, the shell decides. */
|
|
305
|
+
export type ControlRequest =
|
|
306
|
+
| {
|
|
307
|
+
/** Relabel the shell's trusted Submit, with an optional confirmation text. */
|
|
308
|
+
readonly control: "submit";
|
|
309
|
+
readonly label?: string;
|
|
310
|
+
readonly confirm?: string;
|
|
311
|
+
}
|
|
312
|
+
| {
|
|
313
|
+
/** Render a trusted Choose for a review set's candidates (D113). */
|
|
314
|
+
readonly control: "choose";
|
|
315
|
+
readonly reviewSetId: string;
|
|
316
|
+
readonly candidates: readonly { readonly id: string; readonly label?: string }[];
|
|
317
|
+
/** The candidate the page shows now, which Choose names. */
|
|
318
|
+
readonly current?: string;
|
|
319
|
+
}
|
|
320
|
+
| {
|
|
321
|
+
/** Withdraw an earlier request: the Submit label, or a review set's Choose. */
|
|
322
|
+
readonly control: "release";
|
|
323
|
+
readonly submit?: true;
|
|
324
|
+
readonly reviewSetId?: string;
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
/** The longest Submit label the shell renders: one short line. */
|
|
328
|
+
export const CONTROL_LABEL_MAX = 48;
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* `mostra/controls.request`: the artifact asks the shell for a trusted control (D06, SH-02). The
|
|
332
|
+
* shell renders it in its own origin, where the artifact can neither click nor reach it, and
|
|
333
|
+
* answers whether it is shown: a control the host's policy leaves out is not (D108).
|
|
334
|
+
*/
|
|
335
|
+
export const mostraControlsRequest: BridgeMessage<ControlRequest, { readonly shown: boolean }> = {
|
|
336
|
+
method: "mostra/controls.request",
|
|
337
|
+
from: "artifact",
|
|
338
|
+
kind: "request",
|
|
339
|
+
params: z.discriminatedUnion("control", [
|
|
340
|
+
z.looseObject({
|
|
341
|
+
control: z.literal("submit"),
|
|
342
|
+
label: z.string().min(1).max(CONTROL_LABEL_MAX).exactOptional(),
|
|
343
|
+
confirm: z.string().min(1).max(500).exactOptional(),
|
|
344
|
+
}),
|
|
345
|
+
z.looseObject({
|
|
346
|
+
control: z.literal("choose"),
|
|
347
|
+
reviewSetId: z.string().regex(REVIEW_SET_ID_PATTERN),
|
|
348
|
+
candidates: z
|
|
349
|
+
.array(
|
|
350
|
+
z.looseObject({
|
|
351
|
+
id: z.string().min(1).max(256),
|
|
352
|
+
label: z.string().max(CONTROL_LABEL_MAX).exactOptional(),
|
|
353
|
+
}),
|
|
354
|
+
)
|
|
355
|
+
.min(1)
|
|
356
|
+
.max(64)
|
|
357
|
+
.readonly(),
|
|
358
|
+
current: z.string().min(1).max(256).exactOptional(),
|
|
359
|
+
}),
|
|
360
|
+
z.looseObject({
|
|
361
|
+
control: z.literal("release"),
|
|
362
|
+
submit: z.literal(true).exactOptional(),
|
|
363
|
+
reviewSetId: z.string().regex(REVIEW_SET_ID_PATTERN).exactOptional(),
|
|
364
|
+
}),
|
|
365
|
+
]) as unknown as z.ZodType<ControlRequest>,
|
|
366
|
+
result: z.looseObject({ shown: z.boolean() }),
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
/** What the page answers `mostra/flush`. */
|
|
370
|
+
export interface FlushResult {
|
|
371
|
+
/** The viewer's draft revision once every save landed. */
|
|
372
|
+
readonly revision: string;
|
|
373
|
+
/** Whether edits still wait (offline, or a save failed). */
|
|
374
|
+
readonly unsaved: boolean;
|
|
375
|
+
/** What blocks a submit now: invalid values and open conflicts, and required fields the page sees empty. */
|
|
376
|
+
readonly blockers: readonly SubmitBlocker[];
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* `mostra/flush`: the shell asks the page to save everything now, before its trusted Submit or a
|
|
381
|
+
* switch to a newer version (IN-02, D110). For a submit, the page first writes the defaults the
|
|
382
|
+
* viewer saw. The shell checks the answer against the draft it reads itself.
|
|
383
|
+
*/
|
|
384
|
+
export const mostraFlush: BridgeMessage<{ readonly reason: "submit" | "switch" }, FlushResult> = {
|
|
385
|
+
method: "mostra/flush",
|
|
386
|
+
from: "shell",
|
|
387
|
+
kind: "request",
|
|
388
|
+
params: z.looseObject({ reason: z.enum(["submit", "switch"]) }),
|
|
389
|
+
result: z.looseObject({
|
|
390
|
+
revision: z.string().regex(/^(0|[1-9][0-9]{0,15})$/),
|
|
391
|
+
unsaved: z.boolean(),
|
|
392
|
+
blockers: z.array(SubmitBlockerSchema).max(BRIDGE_FIELDS_MAX).readonly(),
|
|
393
|
+
}),
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
/** The params of `mostra/version.available`. */
|
|
397
|
+
export interface VersionAvailableParams {
|
|
398
|
+
/** The newer version's ID. */
|
|
399
|
+
readonly versionId: string;
|
|
400
|
+
/** Its number. */
|
|
401
|
+
readonly version: number;
|
|
402
|
+
/** How many of the viewer's answers it doesn't carry over (D42). */
|
|
403
|
+
readonly orphans: number;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* `mostra/version.available`: a newer version was published while the page is open (VR-04). The
|
|
408
|
+
* shell always shows its own trusted banner; the page may show a notice too.
|
|
409
|
+
*/
|
|
410
|
+
export const mostraVersionAvailable: BridgeMessage<VersionAvailableParams, undefined> = {
|
|
411
|
+
method: "mostra/version.available",
|
|
412
|
+
from: "shell",
|
|
413
|
+
kind: "notification",
|
|
414
|
+
params: z.looseObject({
|
|
415
|
+
versionId: z.string().min(1),
|
|
416
|
+
version: z.int().min(1),
|
|
417
|
+
orphans: z.int().min(0),
|
|
418
|
+
}),
|
|
419
|
+
result: z.undefined(),
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
/** `mostra/field.focus`: the viewer picked a blocker in the shell; the page focuses that field. */
|
|
423
|
+
export const mostraFieldFocus: BridgeMessage<{ readonly id: string }, undefined> = {
|
|
424
|
+
method: "mostra/field.focus",
|
|
425
|
+
from: "shell",
|
|
426
|
+
kind: "notification",
|
|
427
|
+
params: z.looseObject({ id: z.string().min(1).max(256) }),
|
|
428
|
+
result: z.undefined(),
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Every message of the bridge in this release. WP-20 adds review messages; a message never
|
|
433
|
+
* changes meaning once released (D32).
|
|
434
|
+
*/
|
|
435
|
+
export const BRIDGE_MESSAGES: readonly AnyBridgeMessage[] = [
|
|
436
|
+
uiInitialize,
|
|
437
|
+
uiInitialized,
|
|
438
|
+
uiSizeChanged,
|
|
439
|
+
uiOpenLink,
|
|
440
|
+
mostraReady,
|
|
441
|
+
mostraFields,
|
|
442
|
+
mostraPage,
|
|
443
|
+
mostraControlsRequest,
|
|
444
|
+
mostraFlush,
|
|
445
|
+
mostraVersionAvailable,
|
|
446
|
+
mostraFieldFocus,
|
|
447
|
+
] as readonly AnyBridgeMessage[];
|
|
448
|
+
|
|
449
|
+
/** The window events arrive on (`window` itself). */
|
|
450
|
+
export interface BridgeReceiver {
|
|
451
|
+
/** Starts delivering `message` events to `listener`. */
|
|
452
|
+
addEventListener(type: "message", listener: (event: BridgeEvent) => void): void;
|
|
453
|
+
/** Stops delivering them. */
|
|
454
|
+
removeEventListener(type: "message", listener: (event: BridgeEvent) => void): void;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/** The parts of a `MessageEvent` the bridge reads. */
|
|
458
|
+
export interface BridgeEvent {
|
|
459
|
+
/** The message. */
|
|
460
|
+
readonly data: unknown;
|
|
461
|
+
/** The sender's origin, as the browser reports it. */
|
|
462
|
+
readonly origin: string;
|
|
463
|
+
/** The sending window. */
|
|
464
|
+
readonly source: unknown;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/** The window messages go to: the frame's `contentWindow`, or the shell (`window.parent`). */
|
|
468
|
+
export interface BridgePeer {
|
|
469
|
+
/** Posts `message` to the window, delivered only if its origin is `targetOrigin`. */
|
|
470
|
+
postMessage(message: unknown, targetOrigin: string): void;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/** Why the bridge dropped a message, for logs and tests. */
|
|
474
|
+
export type BridgeRejection =
|
|
475
|
+
| "wrong-source"
|
|
476
|
+
| "wrong-origin"
|
|
477
|
+
| "not-json-rpc"
|
|
478
|
+
| "unknown-method"
|
|
479
|
+
| "wrong-direction"
|
|
480
|
+
| "invalid-params";
|
|
481
|
+
|
|
482
|
+
/** Options for {@link connectBridge}. */
|
|
483
|
+
export interface BridgeOptions {
|
|
484
|
+
/** This end. It accepts only messages the other side sends. */
|
|
485
|
+
readonly side: BridgeSide;
|
|
486
|
+
/** Where messages arrive. */
|
|
487
|
+
readonly receiver: BridgeReceiver;
|
|
488
|
+
/** The window this end talks to, looked up on every message (a frame's window changes on reload). */
|
|
489
|
+
readonly peer: () => BridgePeer | null | undefined;
|
|
490
|
+
/** The peer's exact origin. Messages from any other origin are dropped. */
|
|
491
|
+
readonly peerOrigin: string;
|
|
492
|
+
/** Answers a request from the peer; throw an {@link RpcError} to answer with an error. */
|
|
493
|
+
readonly onRequest?: (message: AnyBridgeMessage, params: unknown) => unknown;
|
|
494
|
+
/** Receives a notification from the peer. */
|
|
495
|
+
readonly onNotification?: (message: AnyBridgeMessage, params: unknown) => void;
|
|
496
|
+
/** Hears about every dropped message. */
|
|
497
|
+
readonly onRejected?: (reason: BridgeRejection, event: BridgeEvent) => void;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/** One end of a connected bridge. */
|
|
501
|
+
export interface Bridge {
|
|
502
|
+
/** Sends a request this side may send and resolves with its validated result. */
|
|
503
|
+
call<P, R>(message: BridgeMessage<P, R>, params: P, options?: { timeoutMs?: number }): Promise<R>;
|
|
504
|
+
/** Sends a notification this side may send. */
|
|
505
|
+
notify<P>(message: BridgeMessage<P, undefined>, params: P): void;
|
|
506
|
+
/** Stops listening; waiting calls reject. */
|
|
507
|
+
close(): void;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
const asMethod = <P, R>(message: BridgeMessage<P, R>): MethodDefinition<P, R> => ({
|
|
511
|
+
method: message.method,
|
|
512
|
+
direction: "client-to-instance",
|
|
513
|
+
mutating: false,
|
|
514
|
+
params: message.params,
|
|
515
|
+
result: message.result,
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Connects one end of the bridge. Every incoming message must come from `peer()` at exactly
|
|
520
|
+
* `peerOrigin`, be a JSON-RPC message, name a message the other side sends, and carry valid params.
|
|
521
|
+
*/
|
|
522
|
+
export function connectBridge(options: BridgeOptions): Bridge {
|
|
523
|
+
const known = new Map(BRIDGE_MESSAGES.map((message) => [message.method, message]));
|
|
524
|
+
const send = (message: unknown): void => {
|
|
525
|
+
const peer = options.peer();
|
|
526
|
+
if (!peer) throw new Error("The bridge's peer window is gone.");
|
|
527
|
+
peer.postMessage(message, options.peerOrigin);
|
|
528
|
+
};
|
|
529
|
+
const rpc = new RpcPeer({
|
|
530
|
+
send,
|
|
531
|
+
onRequest: (method, params) => {
|
|
532
|
+
const message = known.get(method);
|
|
533
|
+
if (!message || message.from === options.side || message.kind !== "request") {
|
|
534
|
+
throw new RpcError(
|
|
535
|
+
hostDiagnostic("MOSTRA_HOST_METHOD_UNKNOWN", {
|
|
536
|
+
message: `The bridge has no request ${method} for this side.`,
|
|
537
|
+
}),
|
|
538
|
+
JSON_RPC_ERROR_CODES.methodNotFound,
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
if (!options.onRequest) {
|
|
542
|
+
throw new RpcError(
|
|
543
|
+
hostDiagnostic("MOSTRA_HOST_METHOD_UNKNOWN", {
|
|
544
|
+
message: `This side doesn't answer ${method}.`,
|
|
545
|
+
}),
|
|
546
|
+
JSON_RPC_ERROR_CODES.methodNotFound,
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
const parsed = message.params.safeParse(params ?? {});
|
|
550
|
+
if (!parsed.success) {
|
|
551
|
+
throw new RpcError(
|
|
552
|
+
hostDiagnostic("MOSTRA_HOST_INVALID_PARAMS", {
|
|
553
|
+
message: `Invalid params for ${method}.`,
|
|
554
|
+
}),
|
|
555
|
+
JSON_RPC_ERROR_CODES.invalidParams,
|
|
556
|
+
);
|
|
557
|
+
}
|
|
558
|
+
return options.onRequest(message, parsed.data);
|
|
559
|
+
},
|
|
560
|
+
onNotification: (method, params) => {
|
|
561
|
+
const message = known.get(method);
|
|
562
|
+
if (message?.kind !== "notification" || message.from === options.side) return;
|
|
563
|
+
const parsed = message.params.safeParse(params ?? {});
|
|
564
|
+
if (parsed.success) options.onNotification?.(message, parsed.data);
|
|
565
|
+
},
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
/** Why an incoming message must be dropped, or `undefined` when the RPC peer may take it. */
|
|
569
|
+
const screen = (event: BridgeEvent): BridgeRejection | undefined => {
|
|
570
|
+
const peer = options.peer();
|
|
571
|
+
if (!peer || event.source !== peer) return "wrong-source";
|
|
572
|
+
if (event.origin !== options.peerOrigin) return "wrong-origin";
|
|
573
|
+
const classified = classifyMessage(event.data);
|
|
574
|
+
if (classified.kind === "invalid") return "not-json-rpc";
|
|
575
|
+
if (classified.kind !== "request" && classified.kind !== "notification") return undefined;
|
|
576
|
+
const message = known.get(classified.message.method);
|
|
577
|
+
if (!message) return "unknown-method";
|
|
578
|
+
if (message.from === options.side) return "wrong-direction";
|
|
579
|
+
if (!message.params.safeParse(classified.message.params ?? {}).success) return "invalid-params";
|
|
580
|
+
return undefined;
|
|
581
|
+
};
|
|
582
|
+
const listener = (event: BridgeEvent): void => {
|
|
583
|
+
const rejection = screen(event);
|
|
584
|
+
if (rejection !== undefined) {
|
|
585
|
+
options.onRejected?.(rejection, event);
|
|
586
|
+
// A request from the right window still gets its error answer from the RPC peer.
|
|
587
|
+
const answerable =
|
|
588
|
+
rejection !== "wrong-source" &&
|
|
589
|
+
rejection !== "wrong-origin" &&
|
|
590
|
+
rejection !== "not-json-rpc" &&
|
|
591
|
+
classifyMessage(event.data).kind === "request";
|
|
592
|
+
if (!answerable) return;
|
|
593
|
+
}
|
|
594
|
+
rpc.receive(event.data);
|
|
595
|
+
};
|
|
596
|
+
options.receiver.addEventListener("message", listener);
|
|
597
|
+
|
|
598
|
+
const sendable = (message: AnyBridgeMessage): void => {
|
|
599
|
+
if (message.from !== options.side) {
|
|
600
|
+
throw new Error(`The ${options.side} doesn't send ${message.method}.`);
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
return {
|
|
604
|
+
call(message, params, callOptions) {
|
|
605
|
+
sendable(message as AnyBridgeMessage);
|
|
606
|
+
return rpc.call(asMethod(message), params, callOptions ?? {});
|
|
607
|
+
},
|
|
608
|
+
notify(message, params) {
|
|
609
|
+
sendable(message as AnyBridgeMessage);
|
|
610
|
+
const parsed = message.params.parse(params);
|
|
611
|
+
rpc.notify(message.method, parsed as Readonly<Record<string, unknown>>);
|
|
612
|
+
},
|
|
613
|
+
close() {
|
|
614
|
+
options.receiver.removeEventListener("message", listener);
|
|
615
|
+
rpc.close();
|
|
616
|
+
},
|
|
617
|
+
};
|
|
618
|
+
}
|