diff --git a/packages/cultmesh-ts/src/index.ts b/packages/cultmesh-ts/src/index.ts index ee1b4893..0d1ba965 100644 --- a/packages/cultmesh-ts/src/index.ts +++ b/packages/cultmesh-ts/src/index.ts @@ -6002,6 +6002,37 @@ export class CultMesh { }; } + public static resolveRudpEndpoint( + endpoint: string, + environment: Record = process.env, + ): CultMeshRudpEndpoint { + requireNonEmpty(endpoint, "endpoint"); + const text = endpoint.trim(); + if (text.toLowerCase().startsWith("rudp://")) { + return CultMesh.parseRudpEndpoint(text); + } + if (!text.toLowerCase().startsWith("cultmesh://")) { + return CultMesh.parseRudpEndpoint(`rudp://${text}`); + } + + const uri = new URL(text); + const authority = uri.hostname.trim(); + if (!authority) throw new Error("CultMesh URI must include an authority."); + const slug = authority + .replace(/[^a-z0-9]+/gi, "_") + .replace(/^_+|_+$/g, "") + .toUpperCase(); + for (const key of [`CULTMESH_URI_${slug}_RUDP`, `${slug}_CULTMESH_RUDP_ENDPOINT`]) { + const resolved = environment[key]?.trim(); + if (resolved) { + return CultMesh.parseRudpEndpoint( + resolved.toLowerCase().startsWith("rudp://") ? resolved : `rudp://${resolved}`, + ); + } + } + throw new Error(`CultMesh URI ${text} did not resolve to a RUDP endpoint.`); + } + public static parseRudpEndpoint(endpoint: string): CultMeshRudpEndpoint { requireNonEmpty(endpoint, "endpoint"); const parsed = new URL(endpoint); diff --git a/packages/cultmesh-ts/test/cultmesh.test.ts b/packages/cultmesh-ts/test/cultmesh.test.ts index ab1bc133..07ac0559 100644 --- a/packages/cultmesh-ts/test/cultmesh.test.ts +++ b/packages/cultmesh-ts/test/cultmesh.test.ts @@ -2862,6 +2862,19 @@ test("CultMesh TS publishes one registered document to a RUDP catalog", async () } }); +test("CultMesh TS resolves a CultMesh URI through its authority bootstrap", () => { + assert.deepEqual( + CultMesh.resolveRudpEndpoint("cultmesh://odin/rendezvous/provider-catalog", { + CULTMESH_URI_ODIN_RUDP: "127.0.0.1:17871", + }), + { host: "127.0.0.1", port: 17871, uri: "rudp://127.0.0.1:17871" }, + ); + assert.throws( + () => CultMesh.resolveRudpEndpoint("cultmesh://missing/rendezvous/provider-catalog", {}), + /did not resolve/, + ); +}); + test("CultMesh TS returns a correlated provider receipt after a RUDP document mutation", async () => { const connectionId = 0x10203050; const commandBinding = defineCultNetDocumentBinding({ definition: noteDocument });