feat: expose canonical module display URLs

This commit is contained in:
ekklesia
2026-09-30 18:20:40 -05:00
parent f445a83ea5
commit 3ca6b2cb32
21 changed files with 123 additions and 16 deletions
@@ -52,6 +52,7 @@ export default {
function hostContext(requested: string[], moduleId = "announcements"): ModuleUiHostContext {
return {
contributions: NO_MODULE_UI_CONTRIBUTIONS,
display: undefined,
moduleId,
api: {
async request(path) {
@@ -33,6 +33,7 @@ let artifactSource: string;
function ekklesiaContext(requested: string[]): ModuleUiHostContext {
return {
contributions: NO_MODULE_UI_CONTRIBUTIONS,
display: undefined,
moduleId: "example",
api: {
async request(pathname) {
@@ -50,6 +51,7 @@ function ekklesiaContext(requested: string[]): ModuleUiHostContext {
function standaloneContext(requested: string[]): ModuleUiHostContext {
return {
contributions: NO_MODULE_UI_CONTRIBUTIONS,
display: { url: "https://example.test/display" },
moduleId: "example",
api: {
async request(pathname) {
@@ -75,6 +75,14 @@ export interface ModuleUiHostContext {
/** The module this UI belongs to, as the host knows it. Supplied so a module never has to hardcode
* or re-derive its own identity from a URL. */
readonly moduleId: string;
/**
* The host-owned public page for this module's dedicated display, when one exists.
*
* The URL is already absolute and deployment-correct. A module may show, open, or copy it, but
* must never construct a platform route or combine its own id with a browser origin. A standalone
* host can provide its own display page here without imitating Ekklesia's routing.
*/
readonly display: ModuleUiDisplayLocation | undefined;
/** The only supported way for module UI to reach its own backend. See {@link ModuleUiApiClient}. */
readonly api: ModuleUiApiClient;
/** Who is viewing, for presentation decisions only. See {@link ModuleUiPrincipal} for why this is
@@ -96,6 +104,10 @@ export interface ModuleUiHostContext {
readonly contributions: ModuleUiContributions;
}
export interface ModuleUiDisplayLocation {
readonly url: string;
}
/**
* A module UI's access to its own module's HTTP API, addressed **relative to the module's own API
* root** — never as an absolute path.
@@ -186,6 +186,7 @@ describe("the same module UI artifact hosted by two different hosts", () => {
requestedUrls,
context: {
contributions: NO_MODULE_UI_CONTRIBUTIONS,
display: undefined,
moduleId,
api,
principal: { kind: "anonymous" },
@@ -211,6 +212,7 @@ describe("the same module UI artifact hosted by two different hosts", () => {
requestedUrls,
context: {
contributions: NO_MODULE_UI_CONTRIBUTIONS,
display: { url: "https://announcements.example.org/display" },
moduleId,
api,
principal: { kind: "anonymous" },