import { mxesc1Decode, mxesc1Encode } from './mxesc1'; export type WireProfileName = 'matrix3' | 'mxenv1'; export interface IngressAddress { profile: WireProfileName; root: string; /** @deprecated Use root instead */ realm?: string; shard?: string; // mxenv1 } function assertRootSegment(root: string): void { if (!root || typeof root !== 'string') throw new Error('root must be a non-empty string'); if (root.includes('/')) throw new Error(`root must not contain slashes: ${root}`); if (root.includes('+') || root.includes('#')) throw new Error('root must not contain wildcards'); // DNS roots may contain dots (e.g., 'com.nimbletec.richard.santomauro') } /** Legacy Matrix-3 mailbox: public.ingress. */ export function getLegacyIngressTopic(root: string): string { // legacy format uses mxesc1 because the root may include characters unsafe in a dot-segment. return `public.ingress.${mxesc1Encode(root)}`; } export function extractLegacyRootFromIngressTopic(topic: string): string | null { const prefix = 'public.ingress.'; if (!topic.startsWith(prefix)) return null; try { return mxesc1Decode(topic.slice(prefix.length)); } catch { return null; } } /** @deprecated Use extractLegacyRootFromIngressTopic instead */ export const extractLegacyRealmFromIngressTopic = extractLegacyRootFromIngressTopic; /** DNS-hierarchical ingress topic: {root}.$ingress.{shard} */ export function getMxIngressTopic(root: string, shard: string = '00'): string { assertRootSegment(root); if (!/^[0-9]{2}$/.test(shard)) throw new Error(`invalid shard '${shard}' (expected two digits like '00')`); return `${root}.$ingress.${shard}`; } export function extractMxIngressFromTopic(topic: string): { root: string; shard: string } | null { // Format: {root}.$ingress.{shard} — find the .$ingress. separator const ingressIdx = topic.indexOf('.$ingress.'); if (ingressIdx < 0) return null; const root = topic.slice(0, ingressIdx); const shard = topic.slice(ingressIdx + '.$ingress.'.length); if (!/^[0-9]{2}$/.test(shard)) return null; if (!root) return null; return { root, shard }; } /** * Internal semantic topic — returns the semantic path directly. * With DNS hierarchical addressing, the transport (NatsTransport) handles * root prefix encoding. The federation layer just passes semantic topics. */ export function getInternalTopic(_root: string, toPath: string): string { if (!toPath || typeof toPath !== 'string') throw new Error('toPath must be a non-empty string'); if (toPath.startsWith('/')) throw new Error('toPath must not start with /'); return toPath; }