67 lines
2.6 KiB
TypeScript
Raw Normal View History

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.<mxesc1(root)> */
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;
}