"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.__tsfTypeAliases = exports.MeshLinkRuntime = void 0;
exports.getMeshLinkProcessId = getMeshLinkProcessId;
exports.acquireMeshLinkRuntime = acquireMeshLinkRuntime;
const tslib_1 = require("tslib");
const _a = tslib_1.__importStar(require("@zyno-io/ts-server-foundation/type-metadata-runtime"));
const node_crypto_1 = require("node:crypto");
const ws_1 = tslib_1.__importDefault(require("ws"));
const app_1 = require("../../app");
const helpers_1 = require("../../helpers");
const http_1 = require("../../http");
const types_1 = require("../../srpc/types");
const auth_1 = require("./auth");
const peer_1 = require("./peer");
const protocol_1 = require("./protocol");
const __tsf_metadata_type = _a.createCompactMetadataRegistryV1("[1,[{\"kind\":3},{\"kind\":6},{\"kind\":2},{\"kind\":2,\"typeName\":\"() =\\u003e void\"},{\"kind\":2,\"typeName\":\"(processId: string, endpointId: string) =\\u003e Promise\\u003cstring | MeshLinkResolvedEndpointPin | undefined\\u003e\",\"typeName\":\"MeshLinkEndpointPinResolver\"},{\"kind\":2,\"typeName\":\"(peer: MeshLinkPeer, frame: MeshLinkFrame) =\\u003e Promise\\u003cMeshLinkFrame\\u003e\",\"typeName\":\"MeshRoute\"},{\"kind\":2,\"typeName\":\"(processId: string, endpointId: string) =\\u003e void\"},{\"kind\":18,\"typeName\":\"Omit\",\"utilityType\":\"Omit\",\"typeArguments\":[{\"kind\":18,\"typeName\":\"MeshLinkFrameHeader\",\"types\":[{\"kind\":20,\"name\":\"version\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"type\",\"type\":{\"kind\":12,\"types\":[{\"kind\":10,\"literal\":\"invoke\"},{\"kind\":10,\"literal\":\"reserveStreamIds\"},{\"kind\":10,\"literal\":\"confirmStreamIds\"},{\"kind\":10,\"literal\":\"releaseStreamIds\"},{\"kind\":10,\"literal\":\"streamWrite\"},{\"kind\":10,\"literal\":\"streamFinish\"},{\"kind\":10,\"literal\":\"streamDestroy\"},{\"kind\":10,\"literal\":\"streamActivate\"},{\"kind\":10,\"literal\":\"streamAttach\"},{\"kind\":10,\"literal\":\"revokeCapability\"},{\"kind\":10,\"literal\":\"clientInvoke\"},{\"kind\":10,\"literal\":\"fenceClient\"},{\"kind\":10,\"literal\":\"clientUpdateMetadata\"},{\"kind\":10,\"literal\":\"disconnect\"},{\"kind\":10,\"literal\":\"updateMetadata\"},{\"kind\":10,\"literal\":\"accepted\"},{\"kind\":10,\"literal\":\"result\"},{\"kind\":10,\"literal\":\"ping\"},{\"kind\":10,\"literal\":\"pong\"},{\"kind\":10,\"literal\":\"goAway\"}],\"typeName\":\"MeshLinkFrameType\"},\"optional\":false},{\"kind\":20,\"name\":\"meshKey\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"id\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"replyTo\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"clientId\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"connectionId\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"prefix\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"timeoutMs\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"deadlineAt\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"streamId\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"count\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"ids\",\"type\":{\"kind\":12,\"types\":[{\"kind\":14,\"type\":{\"kind\":7}},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"capability\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"reservationId\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"ok\",\"type\":{\"kind\":12,\"types\":[{\"kind\":8},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"error\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"errorName\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"userError\",\"type\":{\"kind\":12,\"types\":[{\"kind\":8},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"reason\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"closeCapability\",\"type\":{\"kind\":12,\"types\":[{\"kind\":8},{\"kind\":4}]},\"optional\":true}]}],\"utilityKeys\":[\"version\",\"id\"],\"types\":[{\"kind\":20,\"name\":\"type\",\"type\":{\"kind\":12,\"types\":[{\"kind\":10,\"literal\":\"invoke\"},{\"kind\":10,\"literal\":\"reserveStreamIds\"},{\"kind\":10,\"literal\":\"confirmStreamIds\"},{\"kind\":10,\"literal\":\"releaseStreamIds\"},{\"kind\":10,\"literal\":\"streamWrite\"},{\"kind\":10,\"literal\":\"streamFinish\"},{\"kind\":10,\"literal\":\"streamDestroy\"},{\"kind\":10,\"literal\":\"streamActivate\"},{\"kind\":10,\"literal\":\"streamAttach\"},{\"kind\":10,\"literal\":\"revokeCapability\"},{\"kind\":10,\"literal\":\"clientInvoke\"},{\"kind\":10,\"literal\":\"fenceClient\"},{\"kind\":10,\"literal\":\"clientUpdateMetadata\"},{\"kind\":10,\"literal\":\"disconnect\"},{\"kind\":10,\"literal\":\"updateMetadata\"},{\"kind\":10,\"literal\":\"accepted\"},{\"kind\":10,\"literal\":\"result\"},{\"kind\":10,\"literal\":\"ping\"},{\"kind\":10,\"literal\":\"pong\"},{\"kind\":10,\"literal\":\"goAway\"}],\"typeName\":\"MeshLinkFrameType\"},\"optional\":false},{\"kind\":20,\"name\":\"meshKey\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"replyTo\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"clientId\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"connectionId\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"prefix\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"timeoutMs\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"deadlineAt\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"streamId\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"count\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"ids\",\"type\":{\"kind\":12,\"types\":[{\"kind\":14,\"type\":{\"kind\":7}},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"capability\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"reservationId\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"ok\",\"type\":{\"kind\":12,\"types\":[{\"kind\":8},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"error\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"errorName\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"userError\",\"type\":{\"kind\":12,\"types\":[{\"kind\":8},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"reason\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"closeCapability\",\"type\":{\"kind\":12,\"types\":[{\"kind\":8},{\"kind\":4}]},\"optional\":true}]},{\"kind\":7},{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]}]]", []);
const runtimes = new WeakMap();
const processId = (0, node_crypto_1.randomUUID)();
const HandshakeIdentityHeaders = {
    processId: 'x-tsf-mesh-process-id',
    endpointId: 'x-tsf-mesh-endpoint-id',
    timestamp: 'x-tsf-mesh-timestamp',
    nonce: 'x-tsf-mesh-nonce',
    signature: 'x-tsf-mesh-signature',
    requesterEndpointId: 'x-tsf-mesh-requester-endpoint-id',
    requestNonce: 'x-tsf-mesh-request-nonce'
};
function getMeshLinkProcessId() {
    return processId;
}
function acquireMeshLinkRuntime(options) {
    const owner = options.httpServer ?? (0, app_1.getCurrentApp)().http;
    const existing = runtimes.get(owner);
    if (existing) {
        existing.assertCompatible(options);
        return existing;
    }
    const runtime = new MeshLinkRuntime(options, () => runtimes.delete(owner));
    runtimes.set(owner, runtime);
    return runtime;
}
let _MeshLinkRuntimeMetadata_1;
class MeshLinkRuntime {
    options;
    onClose;
    wsServer;
    authenticator;
    routes = new Map();
    endpointPins = new Map();
    endpointPinResolvers = new Set();
    peersByUrl = new Map();
    peersByIdentity = new Map();
    acceptedPeers = new Set();
    allPeers = new Set();
    peerDirections = new WeakMap();
    connectingSockets = new Set();
    peerCloseHandlers = new Set();
    incomingBudget = { activeRequests: 0, activeBytes: 0 };
    cleanupUpgradeHandler;
    idleTimer;
    endpointId = (0, node_crypto_1.randomUUID)();
    endpointKeyPair;
    pendingHandshakes = 0;
    closed = false;
    constructor(options, onClose) {
        this.options = options;
        this.onClose = onClose;
        validateRuntimeOptions(options);
        this.authenticator = new auth_1.MeshLinkAuthenticator(options.secret);
        this.endpointKeyPair = (0, auth_1.createMeshLinkEndpointKeyPair)();
        for (const [endpointId, publicKey] of Object.entries(options.endpointPins ?? {})) {
            this.endpointPins.set(endpointId, { publicKey, expiresAt: Number.POSITIVE_INFINITY, owners: new Set() });
        }
        if (this.endpointPins.size > this.maxEndpointPins)
            throw new Error('sRPC mesh-link endpoint pin limit is exceeded');
        this.wsServer = new ws_1.default.Server({ noServer: true, perMessageDeflate: false, maxPayload: options.maxFrameBytes });
        this.wsServer.on('connection', (ws, request) => this.acceptPeer(ws, request));
        this.wsServer.on('headers', (headers, request) => this.addHandshakeIdentityHeaders(headers, request));
        if (options.httpServer) {
            const handler = (0, http_1.installWebSocketUpgradeHandler)({
                httpServer: options.httpServer,
                wsPath: options.path,
                wsServer: this.wsServer,
                verifyClient: this.verifyClient
            });
            this.cleanupUpgradeHandler = () => (0, http_1.removeWebSocketUpgradeHandler)(options.httpServer, options.path, handler);
        }
        else {
            const handler = (0, http_1.createWebSocketUpgradeHandler)({
                wsPath: options.path,
                wsServer: this.wsServer,
                verifyClient: this.verifyClient
            });
            this.cleanupUpgradeHandler = (0, app_1.getCurrentApp)().http.registerUpgradeHandler(handler);
        }
        this.idleTimer = setInterval(() => this.closeIdlePeers(), Math.max(1000, Math.floor(options.idleTimeoutMs / 2)));
        this.idleTimer.unref?.();
    }
    assertCompatible(options) {
        if (options.path !== this.options.path ||
            options.secret !== this.options.secret ||
            options.connectTimeoutMs !== this.options.connectTimeoutMs ||
            options.idleTimeoutMs !== this.options.idleTimeoutMs ||
            options.maxFrameBytes !== this.options.maxFrameBytes ||
            options.maxBufferedBytes !== this.options.maxBufferedBytes ||
            normalizedResourceLimits(options) !== normalizedResourceLimits(this.options) ||
            normalizedPins(options.endpointPins) !== normalizedPins(this.options.endpointPins)) {
            throw new Error('All sRPC mesh servers sharing an HTTP listener must use identical mesh-link transport configuration');
        }
    }
    get id() {
        return this.endpointId;
    }
    get publicKey() {
        return this.endpointKeyPair.publicKey;
    }
    /** Install a membership pin before accepting or selecting that endpoint. */
    pinEndpoint(endpointId, publicKey, processId, ttlMs = 30000) {
        if (!endpointId || !publicKey)
            throw new Error('sRPC mesh endpoint pin requires an endpoint ID and public key');
        if (!Number.isSafeInteger(ttlMs) || ttlMs < 1 || ttlMs > 2147483647)
            throw new Error('sRPC mesh endpoint pin TTL is invalid');
        const owner = Symbol(endpointId);
        this.pruneEndpointPins();
        const existing = this.endpointPins.get(endpointId);
        if (existing &&
            (existing.owners.size > 0 || existing.expiresAt > Date.now()) &&
            (existing.publicKey !== publicKey || (existing.processId !== undefined && processId !== undefined && existing.processId !== processId))) {
            throw new types_1.SrpcMeshAuthenticationError('Conflicting sRPC mesh endpoint membership pin');
        }
        const pin = existing ?? { publicKey, processId, expiresAt: Date.now() + ttlMs, owners: new Set() };
        if (!existing && this.endpointPins.size >= this.maxEndpointPins) {
            throw new types_1.SrpcMeshAuthenticationError('Too many sRPC mesh endpoint membership pins');
        }
        pin.publicKey = publicKey;
        pin.processId = processId ?? pin.processId;
        pin.expiresAt = Math.max(pin.expiresAt, Date.now() + ttlMs);
        pin.owners.add(owner);
        this.endpointPins.set(endpointId, pin);
        return () => {
            const current = this.endpointPins.get(endpointId);
            if (!current)
                return;
            current.owners.delete(owner);
            if (current.owners.size === 0 && current.expiresAt !== Number.POSITIVE_INFINITY)
                this.endpointPins.delete(endpointId);
        };
    }
    /** Resolve a missing pin from a live membership source during the WebSocket
     * handshake. The resolver must validate the process+endpoint lease itself. */
    registerEndpointPinResolver(resolver) {
        this.endpointPinResolvers.add(resolver);
        return () => this.endpointPinResolvers.delete(resolver);
    }
    get isClosed() {
        return this.closed;
    }
    register(meshKey, route) {
        if (this.closed)
            throw new Error('sRPC mesh runtime is closed');
        if (this.routes.has(meshKey))
            throw new Error(`sRPC mesh key is already registered in this process: ${meshKey}`);
        this.routes.set(meshKey, route);
        return () => {
            if (this.routes.get(meshKey) === route)
                this.routes.delete(meshKey);
            this.closeIfUnused();
        };
    }
    onPeerClosed(handler) {
        this.peerCloseHandlers.add(handler);
        return () => this.peerCloseHandlers.delete(handler);
    }
    async request(url, header, body, timeoutMs, remoteProcessId, remoteEndpointId, remoteEndpointPublicKey) {
        this.assertOpen();
        (0, helpers_1.assertSafeTimerMs)(timeoutMs, 'sRPC mesh request timeout');
        const clientId = header.clientId ?? 'unknown';
        const deadlineAt = Date.now() + timeoutMs;
        const peer = await this.getPeer(url, deadlineAt, clientId, remoteProcessId, remoteEndpointId, remoteEndpointPublicKey);
        this.assertOpen();
        const dispatchTimeoutMs = remainingRequestMs(deadlineAt, clientId);
        return peer.request({
            ...header,
            ...(header.timeoutMs !== undefined ? { timeoutMs: Math.min(header.timeoutMs, dispatchTimeoutMs) } : {})
        }, body, dispatchTimeoutMs);
    }
    /**
     * Send a reverse tunnel frame to the current winning link for an
     * authenticated identity.  Do not retain a peer object here: duplicate
     * link arbitration can legitimately replace it while a tunnel is open.
     */
    async requestPeer(remoteProcessId, remoteEndpointId, header, body, timeoutMs, remoteEndpointPublicKey) {
        this.assertOpen();
        if (remoteEndpointId !== undefined && !remoteEndpointPublicKey) {
            throw new types_1.SrpcMeshAuthenticationError('sRPC mesh reverse routing requires the endpoint membership key');
        }
        const matches = [...this.peersByIdentity.values()].filter(candidate => candidate.peer.processId === remoteProcessId &&
            (remoteEndpointId === undefined || candidate.peer.endpointId === remoteEndpointId) &&
            (remoteEndpointPublicKey === undefined || candidate.peer.publicKey === remoteEndpointPublicKey));
        const peer = matches.length === 1 ? matches[0].peer : undefined;
        if (!peer?.connected)
            throw new types_1.SrpcOwnerUnavailableError(header.clientId ?? 'unknown');
        return peer.request({
            ...header,
            ...(header.timeoutMs !== undefined ? { timeoutMs: Math.min(header.timeoutMs, timeoutMs) } : {})
        }, body, timeoutMs);
    }
    closePeer(remoteProcessId, remoteEndpointId, reason, remoteEndpointPublicKey) {
        if (!remoteProcessId && !remoteEndpointId)
            return;
        if (remoteEndpointId !== undefined && !remoteEndpointPublicKey) {
            throw new types_1.SrpcMeshAuthenticationError('sRPC mesh peer close requires the endpoint membership key');
        }
        for (const { peer } of this.peersByIdentity.values()) {
            if ((remoteProcessId === undefined || peer.processId === remoteProcessId) &&
                (remoteEndpointId === undefined || peer.endpointId === remoteEndpointId) &&
                (remoteEndpointPublicKey === undefined || peer.publicKey === remoteEndpointPublicKey)) {
                peer.close(1012, reason);
            }
        }
    }
    close() {
        if (this.closed)
            return;
        this.closed = true;
        clearInterval(this.idleTimer);
        this.cleanupUpgradeHandler();
        for (const peer of this.allPeers)
            peer.close(1001, 'sRPC mesh runtime stopped');
        this.acceptedPeers.clear();
        for (const socket of this.connectingSockets)
            socket.terminate();
        this.connectingSockets.clear();
        for (const peerPromise of this.peersByUrl.values())
            void peerPromise.then(peer => peer.close(1001, 'sRPC mesh runtime stopped'), () => { });
        this.peersByUrl.clear();
        this.peersByIdentity.clear();
        this.allPeers.clear();
        this.routes.clear();
        this.peerCloseHandlers.clear();
        this.wsServer.close();
        this.onClose?.();
    }
    closeIfUnused() {
        if (this.routes.size === 0)
            this.close();
    }
    verifyClient = (info, cb) => {
        if (this.closed || this.pendingHandshakes >= this.maxInboundPeers || !this.canCreatePeer('inbound', this.pendingHandshakes)) {
            cb(false, 503, 'Mesh peer capacity reached');
            return;
        }
        this.pendingHandshakes++;
        void this.verifyIncomingClient(info.req)
            .then(() => cb(!this.closed, this.closed ? 503 : undefined, this.closed ? 'Mesh runtime closed' : undefined), error => cb(false, error instanceof types_1.SrpcMeshAuthenticationError ? 403 : 400, 'Mesh authentication failed'))
            .finally(() => {
            this.pendingHandshakes = Math.max(0, this.pendingHandshakes - 1);
        });
    };
    async verifyIncomingClient(request) {
        try {
            const url = new URL(request.url ?? '', 'http://localhost');
            readProtocolVersion(url);
            const identity = readIdentity(url);
            // Direct runtime callers may authenticate an endpoint with a local
            // membership pin without knowing its endpoint ID in advance. Bind
            // the proof to this endpoint only when the caller supplied an
            // audience; the endpoint proof and membership-pin checks below
            // still authenticate every request in either case.
            this.verifyEndpointProof(identity, 'request', undefined, identity.audienceEndpointId === undefined ? undefined : this.endpointId, true);
            await this.authorizeEndpointPin(identity);
            // Only a proof authorized by membership may consume replay-cache
            // capacity. A group-secret holder cannot saturate it with invalid
            // endpoint signatures.
            this.authenticator.verify(identity, this.options.path, 'request');
        }
        catch (error) {
            throw error;
        }
    }
    acceptPeer(ws, request) {
        const url = new URL(request.url ?? '', 'http://localhost');
        const identity = readIdentity(url);
        if (!this.canCreatePeer('inbound')) {
            ws.close(1013, 'sRPC mesh inbound peer limit reached');
            return;
        }
        const peer = this.createPeer(identity.processId, ws, 'inbound', identity.endpointId ?? identity.processId, identity.endpointPublicKey ?? '');
        this.acceptedPeers.add(peer);
        ws.once('close', () => this.acceptedPeers.delete(peer));
        this.resolveDuplicatePeer(identity.processId, peer, 'inbound');
    }
    async getPeer(rawUrl, deadlineAt, clientId, remoteProcessId, remoteEndpointId, remoteEndpointPublicKey) {
        this.assertOpen();
        const url = new URL(rawUrl);
        const key = meshUrlKey(url);
        const existing = this.peersByUrl.get(key);
        if (existing) {
            this.peersByUrl.delete(key);
            this.peersByUrl.set(key, existing);
            const peer = await waitForPeer(existing, deadlineAt, clientId);
            if (peer.connected &&
                (!remoteProcessId || peer.processId === remoteProcessId) &&
                (!remoteEndpointId || peer.endpointId === remoteEndpointId) &&
                (!remoteEndpointPublicKey || peer.publicKey === remoteEndpointPublicKey)) {
                return peer;
            }
            if (peer.connected)
                peer.close(1000, 'sRPC mesh endpoint identity changed');
            this.peersByUrl.delete(key);
        }
        if (this.peersByUrl.size >= this.maxCachedUrls)
            this.evictUrlCacheEntry();
        const connecting = this.connect(url, remoteProcessId, remoteEndpointId, remoteEndpointPublicKey);
        this.peersByUrl.set(key, connecting);
        void connecting.catch(() => {
            if (this.peersByUrl.get(key) === connecting)
                this.peersByUrl.delete(key);
        });
        return waitForPeer(connecting, deadlineAt, clientId);
    }
    connect(url, remoteProcessId, remoteEndpointId, remoteEndpointPublicKey) {
        if (!this.canCreatePeer('outbound')) {
            return Promise.reject(new types_1.SrpcOwnerUnavailableError('unknown', new Error('sRPC mesh outbound peer limit reached')));
        }
        const identity = this.authenticator.createIdentity(processId, this.options.path, 'request', this.endpointId, undefined, remoteEndpointId);
        url.searchParams.set('protocolVersion', String(protocol_1.MeshLinkProtocolVersion));
        url.searchParams.set('processId', identity.processId);
        url.searchParams.set('endpointId', identity.endpointId);
        url.searchParams.set('timestamp', String(identity.timestamp));
        url.searchParams.set('nonce', identity.nonce);
        url.searchParams.set('signature', identity.signature);
        if (identity.audienceEndpointId)
            url.searchParams.set('audienceEndpointId', identity.audienceEndpointId);
        this.addEndpointProof(url.searchParams, identity, 'request');
        return new Promise((resolve, reject) => {
            const ws = new ws_1.default(url, {
                perMessageDeflate: false,
                handshakeTimeout: this.options.connectTimeoutMs,
                maxPayload: this.options.maxFrameBytes
            });
            this.connectingSockets.add(ws);
            let serverIdentity;
            let settled = false;
            const fail = (error) => {
                if (settled)
                    return;
                settled = true;
                clearTimeout(timeout);
                this.connectingSockets.delete(ws);
                ws.terminate();
                reject(error);
            };
            const timeout = setTimeout(() => {
                fail(new types_1.SrpcOwnerUnavailableError('unknown', new Error('sRPC mesh link connection timed out')));
            }, this.options.connectTimeoutMs);
            timeout.unref?.();
            ws.once('upgrade', response => {
                try {
                    serverIdentity = readHandshakeIdentity(response.headers);
                    this.verifyEndpointProof(serverIdentity, 'response', {
                        endpointId: this.endpointId,
                        nonce: identity.nonce
                    });
                    if (remoteProcessId && serverIdentity.processId !== remoteProcessId) {
                        throw new types_1.SrpcMeshAuthenticationError('sRPC mesh server identity does not match the expected process');
                    }
                    if (remoteEndpointId && serverIdentity.endpointId !== remoteEndpointId) {
                        throw new types_1.SrpcMeshAuthenticationError('sRPC mesh server identity does not match the expected endpoint');
                    }
                    if (remoteEndpointPublicKey && serverIdentity.endpointPublicKey !== remoteEndpointPublicKey) {
                        throw new types_1.SrpcMeshAuthenticationError('sRPC mesh server endpoint key does not match the expected membership pin');
                    }
                    this.authenticator.verify(serverIdentity, this.options.path, 'response', {
                        endpointId: this.endpointId,
                        nonce: identity.nonce
                    });
                }
                catch (error) {
                    fail(error instanceof Error ? error : new types_1.SrpcMeshAuthenticationError());
                }
            });
            ws.once('open', () => {
                if (settled)
                    return;
                if (!serverIdentity) {
                    fail(new types_1.SrpcMeshAuthenticationError('sRPC mesh server did not provide a handshake identity'));
                    return;
                }
                if (this.closed) {
                    fail(new types_1.SrpcOwnerUnavailableError('unknown', new Error('sRPC mesh runtime is closed')));
                    return;
                }
                settled = true;
                clearTimeout(timeout);
                this.connectingSockets.delete(ws);
                const peer = this.createPeer(serverIdentity.processId, ws, 'outbound', serverIdentity.endpointId, serverIdentity.endpointPublicKey);
                resolve(this.resolveDuplicatePeer(serverIdentity.processId, peer, 'outbound'));
            });
            ws.once('error', error => {
                fail(new types_1.SrpcOwnerUnavailableError('unknown', error));
            });
        });
    }
    createPeer(remoteProcessId, ws, direction, remoteEndpointId = remoteProcessId, remoteEndpointPublicKey = '') {
        const handler = async (peer, frame) => {
            const meshKey = frame.header.meshKey;
            const route = meshKey ? this.routes.get(meshKey) : undefined;
            if (!route)
                throw new Error(`No local sRPC mesh route for key: ${meshKey ?? '(missing)'}`);
            return route(peer, frame);
        };
        const peer = new peer_1.MeshLinkPeer(remoteProcessId, ws, this.options.maxFrameBytes, this.options.maxBufferedBytes, handler, remoteEndpointId, this.incomingBudget, remoteEndpointPublicKey);
        this.allPeers.add(peer);
        this.peerDirections.set(peer, direction);
        peer.onClose(() => {
            this.allPeers.delete(peer);
            this.pruneEndpointPins();
            const identity = peerIdentity(peer);
            if (this.peersByIdentity.get(identity)?.peer === peer)
                this.peersByIdentity.delete(identity);
            if ([...this.allPeers].some(candidate => peerIdentity(candidate) === identity && candidate.connected))
                return;
            for (const closeHandler of this.peerCloseHandlers) {
                try {
                    closeHandler(remoteProcessId, remoteEndpointId);
                }
                catch {
                    // Consumer callbacks must not prevent transport cleanup.
                }
            }
        });
        return peer;
    }
    resolveDuplicatePeer(remoteProcessId, peer, direction) {
        const identity = peerIdentity(peer);
        const existing = this.peersByIdentity.get(identity);
        if (!existing?.peer.connected) {
            this.peersByIdentity.set(identity, { peer, direction });
            return peer;
        }
        const preferredDirection = this.endpointId < peer.endpointId ? 'outbound' : 'inbound';
        if (direction === preferredDirection && existing.direction !== preferredDirection) {
            this.peersByIdentity.set(identity, { peer, direction });
            existing.peer.close(1000, 'Duplicate sRPC mesh link');
            return peer;
        }
        else {
            peer.close(1000, 'Duplicate sRPC mesh link');
            return existing.peer;
        }
    }
    assertOpen() {
        if (this.closed)
            throw new Error('sRPC mesh runtime is closed');
    }
    addHandshakeIdentityHeaders(headers, request) {
        const requestIdentity = readIdentity(new URL(request.url ?? '', 'http://localhost'));
        const identity = this.authenticator.createIdentity(processId, this.options.path, 'response', this.endpointId, {
            endpointId: requestIdentity.endpointId ?? '',
            nonce: requestIdentity.nonce
        });
        const proof = this.endpointProof(identity, 'response', {
            endpointId: requestIdentity.endpointId ?? '',
            nonce: requestIdentity.nonce
        });
        headers.push(`${HandshakeIdentityHeaders.processId}: ${identity.processId}`, `${HandshakeIdentityHeaders.endpointId}: ${identity.endpointId}`, `${HandshakeIdentityHeaders.timestamp}: ${identity.timestamp}`, `${HandshakeIdentityHeaders.nonce}: ${identity.nonce}`, `${HandshakeIdentityHeaders.signature}: ${identity.signature}`, `${HandshakeIdentityHeaders.requesterEndpointId}: ${identity.requesterEndpointId}`, `${HandshakeIdentityHeaders.requestNonce}: ${identity.requestNonce}`, `x-tsf-mesh-endpoint-public-key: ${proof.publicKey}`, `x-tsf-mesh-endpoint-signature: ${proof.signature}`);
    }
    closeIdlePeers() {
        this.pruneEndpointPins();
        const deadline = Date.now() - this.options.idleTimeoutMs;
        for (const peer of this.allPeers) {
            if (!peer.connected || (peer.idleSince < deadline && !peer.hasActiveWork))
                peer.close(1000, 'sRPC mesh link idle');
        }
        for (const [url, peerPromise] of this.peersByUrl) {
            void peerPromise.then(peer => {
                if (!peer.connected && this.peersByUrl.get(url) === peerPromise)
                    this.peersByUrl.delete(url);
            }, () => {
                if (this.peersByUrl.get(url) === peerPromise)
                    this.peersByUrl.delete(url);
            });
        }
    }
    get maxPeers() {
        return this.options.maxPeers ?? 1024;
    }
    get maxEndpointPins() {
        return this.options.maxEndpointPins ?? this.maxPeers * 2;
    }
    get maxInboundPeers() {
        return this.options.maxInboundPeers ?? Math.max(1, Math.floor(this.maxPeers / 2));
    }
    get maxOutboundPeers() {
        return this.options.maxOutboundPeers ?? this.maxPeers - this.maxInboundPeers;
    }
    get maxCachedUrls() {
        return this.options.maxCachedUrls ?? this.maxPeers;
    }
    canCreatePeer(direction, pending = 0) {
        if (this.allPeers.size + this.connectingSockets.size + pending >= this.maxPeers)
            return false;
        let matching = 0;
        for (const peer of this.allPeers) {
            if (this.peerDirections.get(peer) === direction)
                matching++;
        }
        if (direction === 'outbound')
            matching += this.connectingSockets.size;
        return matching + pending < (direction === 'inbound' ? this.maxInboundPeers : this.maxOutboundPeers);
    }
    evictUrlCacheEntry() {
        const candidate = this.peersByUrl.entries().next().value;
        if (!candidate)
            return;
        const [key, peerPromise] = candidate;
        this.peersByUrl.delete(key);
        // The identity map may still own this peer and other callers may be using
        // it. Cache eviction drops only the URL reference.
        void peerPromise.catch(() => { });
    }
    addEndpointProof(params, identity, purpose) {
        const proof = this.endpointProof(identity, purpose, purpose === 'response' ? { endpointId: identity.requesterEndpointId ?? '', nonce: identity.requestNonce ?? '' } : undefined);
        params.set('endpointPublicKey', proof.publicKey);
        params.set('endpointSignature', proof.signature);
    }
    endpointProof(identity, purpose, responseTo) {
        const proofIdentity = { ...identity, endpointPublicKey: this.endpointKeyPair.publicKey };
        return {
            publicKey: this.endpointKeyPair.publicKey,
            signature: (0, auth_1.signMeshLinkEndpointProof)(this.endpointKeyPair.privateKey, proofIdentity, this.options.path, purpose, responseTo)
        };
    }
    verifyEndpointProof(identity, purpose, responseTo, audienceEndpointId, deferPinAuthorization = false) {
        if (!identity.endpointId || !identity.endpointPublicKey || !identity.endpointSignature) {
            throw new types_1.SrpcMeshAuthenticationError('sRPC mesh endpoint proof is missing');
        }
        if (!(0, auth_1.verifyMeshLinkEndpointProof)(identity, this.options.path, purpose, responseTo)) {
            throw new types_1.SrpcMeshAuthenticationError('sRPC mesh endpoint proof is invalid');
        }
        if (purpose === 'request' && audienceEndpointId && identity.audienceEndpointId !== audienceEndpointId) {
            throw new types_1.SrpcMeshAuthenticationError('sRPC mesh request proof was issued for another endpoint');
        }
        if (deferPinAuthorization)
            return;
        const pin = this.getEndpointPin(identity.processId, identity.endpointId);
        if (pin === undefined || pin.publicKey !== identity.endpointPublicKey) {
            throw new types_1.SrpcMeshAuthenticationError('sRPC mesh endpoint key does not match its membership pin');
        }
    }
    async authorizeEndpointPin(identity) {
        const endpointId = identity.endpointId;
        const publicKey = identity.endpointPublicKey;
        const pinned = this.getEndpointPin(identity.processId, endpointId);
        if (pinned !== undefined) {
            if (pinned.publicKey !== publicKey)
                throw new types_1.SrpcMeshAuthenticationError('sRPC mesh endpoint key does not match its membership pin');
            return;
        }
        for (const resolver of this.endpointPinResolvers) {
            const result = await withTimeout(resolver(identity.processId, endpointId), this.options.connectTimeoutMs, 'sRPC mesh endpoint pin resolver timed out');
            if (this.closed)
                throw new types_1.SrpcOwnerUnavailableError('unknown', new Error('sRPC mesh runtime is closed'));
            if (result === undefined)
                continue;
            const resolved = typeof result === 'string' ? { publicKey: result, expiresAt: Date.now() + 1000 } : result;
            if (resolved.publicKey !== publicKey || !Number.isFinite(resolved.expiresAt) || resolved.expiresAt <= Date.now()) {
                throw new types_1.SrpcMeshAuthenticationError('sRPC mesh endpoint key does not match its live membership pin');
            }
            this.pruneEndpointPins();
            if (this.endpointPins.size >= this.maxEndpointPins) {
                throw new types_1.SrpcMeshAuthenticationError('Too many sRPC mesh endpoint membership pins');
            }
            this.endpointPins.set(endpointId, {
                publicKey: resolved.publicKey,
                processId: identity.processId,
                expiresAt: resolved.expiresAt,
                owners: new Set()
            });
            return;
        }
        throw new types_1.SrpcMeshAuthenticationError('sRPC mesh endpoint has no live membership pin');
    }
    getEndpointPin(processId, endpointId) {
        const pin = this.endpointPins.get(endpointId);
        if (!pin)
            return undefined;
        if (pin.processId !== undefined && pin.processId !== processId)
            return undefined;
        if (pin.expiresAt <= Date.now() && pin.owners.size === 0) {
            this.endpointPins.delete(endpointId);
            return undefined;
        }
        return pin;
    }
    pruneEndpointPins() {
        const now = Date.now();
        for (const [endpointId, pin] of this.endpointPins) {
            if (pin.owners.size === 0 && pin.expiresAt <= now)
                this.endpointPins.delete(endpointId);
        }
    }
    static __tsfType = _MeshLinkRuntimeMetadata_1 = _a.decodeCompactMetadataV1("[1,{\"kind\":16,\"name\":\"MeshLinkRuntime\",\"typeName\":\"MeshLinkRuntime\",\"classType\":{\"$tsf\":0},\"properties\":[],\"methods\":[{\"name\":\"assertCompatible\",\"parameters\":[{\"name\":\"options\",\"type\":{\"kind\":18,\"typeName\":\"MeshLinkRuntimeOptions\",\"types\":[{\"kind\":20,\"name\":\"path\",\"type\":{\"kind\":6},\"optional\":false},{\"kind\":20,\"name\":\"secret\",\"type\":{\"kind\":6},\"optional\":false},{\"kind\":20,\"name\":\"httpServer\",\"type\":{\"kind\":12,\"types\":[{\"$tsfAlias\":[1,\"node:http\",\"Server\",\"Server\"]},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"connectTimeoutMs\",\"type\":{\"kind\":7},\"optional\":false},{\"kind\":20,\"name\":\"idleTimeoutMs\",\"type\":{\"kind\":7},\"optional\":false},{\"kind\":20,\"name\":\"maxFrameBytes\",\"type\":{\"kind\":7},\"optional\":false},{\"kind\":20,\"name\":\"maxBufferedBytes\",\"type\":{\"kind\":7},\"optional\":false},{\"kind\":20,\"name\":\"maxPeers\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"maxInboundPeers\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"maxOutboundPeers\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"maxCachedUrls\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"maxEndpointPins\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"endpointPins\",\"type\":{\"kind\":12,\"types\":[{\"kind\":16,\"typeName\":\"Readonly\",\"classType\":{\"$tsf\":2},\"typeArguments\":[{\"kind\":18,\"typeName\":\"Record\",\"utilityType\":\"Record\",\"typeArguments\":[{\"kind\":6},{\"kind\":6}],\"index\":{\"kind\":6},\"types\":[]}]},{\"kind\":4}]},\"optional\":true}]},\"optional\":false,\"default\":false}],\"returnType\":{\"$tsfType\":0}},{\"name\":\"pinEndpoint\",\"parameters\":[{\"name\":\"endpointId\",\"type\":{\"$tsfType\":1},\"optional\":false,\"default\":false},{\"name\":\"publicKey\",\"type\":{\"$tsfType\":1},\"optional\":false,\"default\":false},{\"name\":\"processId\",\"type\":{\"$tsfType\":1},\"optional\":true,\"default\":false},{\"name\":\"ttlMs\",\"type\":{\"$tsfType\":2},\"optional\":true,\"default\":true}],\"returnType\":{\"$tsfType\":3},\"description\":\"Install a membership pin before accepting or selecting that endpoint.\"},{\"name\":\"registerEndpointPinResolver\",\"parameters\":[{\"name\":\"resolver\",\"type\":{\"$tsfType\":4},\"optional\":false,\"default\":false}],\"returnType\":{\"$tsfType\":3},\"description\":\"Resolve a missing pin from a live membership source during the WebSocket handshake. The resolver must validate the process+endpoint lease itself.\"},{\"name\":\"register\",\"parameters\":[{\"name\":\"meshKey\",\"type\":{\"$tsfType\":1},\"optional\":false,\"default\":false},{\"name\":\"route\",\"type\":{\"$tsfType\":5},\"optional\":false,\"default\":false}],\"returnType\":{\"$tsfType\":3}},{\"name\":\"onPeerClosed\",\"parameters\":[{\"name\":\"handler\",\"type\":{\"$tsfType\":6},\"optional\":false,\"default\":false}],\"returnType\":{\"$tsfType\":3}},{\"name\":\"request\",\"parameters\":[{\"name\":\"url\",\"type\":{\"$tsfType\":1},\"optional\":false,\"default\":false},{\"name\":\"header\",\"type\":{\"$tsfType\":7},\"optional\":false,\"default\":false},{\"name\":\"body\",\"type\":{\"kind\":16,\"typeName\":\"Uint8Array\",\"classType\":{\"$tsf\":3}},\"optional\":false,\"default\":false},{\"name\":\"timeoutMs\",\"type\":{\"$tsfType\":8},\"optional\":false,\"default\":false},{\"name\":\"remoteProcessId\",\"type\":{\"$tsfType\":1},\"optional\":true,\"default\":false},{\"name\":\"remoteEndpointId\",\"type\":{\"$tsfType\":1},\"optional\":true,\"default\":false},{\"name\":\"remoteEndpointPublicKey\",\"type\":{\"$tsfType\":1},\"optional\":true,\"default\":false}],\"returnType\":{\"kind\":22,\"type\":{\"kind\":18,\"typeName\":\"MeshLinkFrame\",\"types\":[{\"kind\":20,\"name\":\"header\",\"type\":{\"kind\":18,\"typeName\":\"MeshLinkFrameHeader\",\"types\":[{\"kind\":20,\"name\":\"version\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"type\",\"type\":{\"kind\":12,\"types\":[{\"kind\":10,\"literal\":\"invoke\"},{\"kind\":10,\"literal\":\"reserveStreamIds\"},{\"kind\":10,\"literal\":\"confirmStreamIds\"},{\"kind\":10,\"literal\":\"releaseStreamIds\"},{\"kind\":10,\"literal\":\"streamWrite\"},{\"kind\":10,\"literal\":\"streamFinish\"},{\"kind\":10,\"literal\":\"streamDestroy\"},{\"kind\":10,\"literal\":\"streamActivate\"},{\"kind\":10,\"literal\":\"streamAttach\"},{\"kind\":10,\"literal\":\"revokeCapability\"},{\"kind\":10,\"literal\":\"clientInvoke\"},{\"kind\":10,\"literal\":\"fenceClient\"},{\"kind\":10,\"literal\":\"clientUpdateMetadata\"},{\"kind\":10,\"literal\":\"disconnect\"},{\"kind\":10,\"literal\":\"updateMetadata\"},{\"kind\":10,\"literal\":\"accepted\"},{\"kind\":10,\"literal\":\"result\"},{\"kind\":10,\"literal\":\"ping\"},{\"kind\":10,\"literal\":\"pong\"},{\"kind\":10,\"literal\":\"goAway\"}],\"typeName\":\"MeshLinkFrameType\"},\"optional\":false},{\"kind\":20,\"name\":\"meshKey\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"id\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"replyTo\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"clientId\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"connectionId\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"prefix\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"timeoutMs\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"deadlineAt\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"streamId\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"count\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"ids\",\"type\":{\"kind\":12,\"types\":[{\"kind\":14,\"type\":{\"kind\":7}},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"capability\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"reservationId\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"ok\",\"type\":{\"kind\":12,\"types\":[{\"kind\":8},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"error\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"errorName\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"userError\",\"type\":{\"kind\":12,\"types\":[{\"kind\":8},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"reason\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"closeCapability\",\"type\":{\"kind\":12,\"types\":[{\"kind\":8},{\"kind\":4}]},\"optional\":true}]},\"optional\":false},{\"kind\":20,\"name\":\"body\",\"type\":{\"kind\":16,\"typeName\":\"Buffer\",\"classType\":{\"$tsf\":4}},\"optional\":false}]}}},{\"name\":\"requestPeer\",\"parameters\":[{\"name\":\"remoteProcessId\",\"type\":{\"$tsfType\":1},\"optional\":false,\"default\":false},{\"name\":\"remoteEndpointId\",\"type\":{\"$tsfType\":9},\"optional\":false,\"default\":false},{\"name\":\"header\",\"type\":{\"$tsfType\":7},\"optional\":false,\"default\":false},{\"name\":\"body\",\"type\":{\"kind\":16,\"typeName\":\"Uint8Array\",\"classType\":{\"$tsf\":3}},\"optional\":false,\"default\":false},{\"name\":\"timeoutMs\",\"type\":{\"$tsfType\":8},\"optional\":false,\"default\":false},{\"name\":\"remoteEndpointPublicKey\",\"type\":{\"$tsfType\":1},\"optional\":true,\"default\":false}],\"returnType\":{\"kind\":22,\"type\":{\"kind\":18,\"typeName\":\"MeshLinkFrame\",\"types\":[{\"kind\":20,\"name\":\"header\",\"type\":{\"kind\":18,\"typeName\":\"MeshLinkFrameHeader\",\"types\":[{\"kind\":20,\"name\":\"version\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"type\",\"type\":{\"kind\":12,\"types\":[{\"kind\":10,\"literal\":\"invoke\"},{\"kind\":10,\"literal\":\"reserveStreamIds\"},{\"kind\":10,\"literal\":\"confirmStreamIds\"},{\"kind\":10,\"literal\":\"releaseStreamIds\"},{\"kind\":10,\"literal\":\"streamWrite\"},{\"kind\":10,\"literal\":\"streamFinish\"},{\"kind\":10,\"literal\":\"streamDestroy\"},{\"kind\":10,\"literal\":\"streamActivate\"},{\"kind\":10,\"literal\":\"streamAttach\"},{\"kind\":10,\"literal\":\"revokeCapability\"},{\"kind\":10,\"literal\":\"clientInvoke\"},{\"kind\":10,\"literal\":\"fenceClient\"},{\"kind\":10,\"literal\":\"clientUpdateMetadata\"},{\"kind\":10,\"literal\":\"disconnect\"},{\"kind\":10,\"literal\":\"updateMetadata\"},{\"kind\":10,\"literal\":\"accepted\"},{\"kind\":10,\"literal\":\"result\"},{\"kind\":10,\"literal\":\"ping\"},{\"kind\":10,\"literal\":\"pong\"},{\"kind\":10,\"literal\":\"goAway\"}],\"typeName\":\"MeshLinkFrameType\"},\"optional\":false},{\"kind\":20,\"name\":\"meshKey\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"id\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"replyTo\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"clientId\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"connectionId\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"prefix\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"timeoutMs\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"deadlineAt\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"streamId\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"count\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"ids\",\"type\":{\"kind\":12,\"types\":[{\"kind\":14,\"type\":{\"kind\":7}},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"capability\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"reservationId\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"ok\",\"type\":{\"kind\":12,\"types\":[{\"kind\":8},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"error\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"errorName\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"userError\",\"type\":{\"kind\":12,\"types\":[{\"kind\":8},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"reason\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"closeCapability\",\"type\":{\"kind\":12,\"types\":[{\"kind\":8},{\"kind\":4}]},\"optional\":true}]},\"optional\":false},{\"kind\":20,\"name\":\"body\",\"type\":{\"kind\":16,\"typeName\":\"Buffer\",\"classType\":{\"$tsf\":4}},\"optional\":false}]}},\"description\":\"Send a reverse tunnel frame to the current winning link for an authenticated identity.  Do not retain a peer object here: duplicate link arbitration can legitimately replace it while a tunnel is open.\"},{\"name\":\"closePeer\",\"parameters\":[{\"name\":\"remoteProcessId\",\"type\":{\"$tsfType\":9},\"optional\":false,\"default\":false},{\"name\":\"remoteEndpointId\",\"type\":{\"$tsfType\":9},\"optional\":false,\"default\":false},{\"name\":\"reason\",\"type\":{\"$tsfType\":1},\"optional\":false,\"default\":false},{\"name\":\"remoteEndpointPublicKey\",\"type\":{\"$tsfType\":1},\"optional\":true,\"default\":false}],\"returnType\":{\"$tsfType\":0}},{\"name\":\"close\",\"parameters\":[],\"returnType\":{\"$tsfType\":0}}],\"hasConstructor\":true,\"constructorParameters\":[{\"name\":\"options\",\"type\":{\"kind\":18,\"typeName\":\"MeshLinkRuntimeOptions\",\"types\":[{\"kind\":20,\"name\":\"path\",\"type\":{\"kind\":6},\"optional\":false},{\"kind\":20,\"name\":\"secret\",\"type\":{\"kind\":6},\"optional\":false},{\"kind\":20,\"name\":\"httpServer\",\"type\":{\"kind\":12,\"types\":[{\"$tsfAlias\":[1,\"node:http\",\"Server\",\"Server\"]},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"connectTimeoutMs\",\"type\":{\"kind\":7},\"optional\":false},{\"kind\":20,\"name\":\"idleTimeoutMs\",\"type\":{\"kind\":7},\"optional\":false},{\"kind\":20,\"name\":\"maxFrameBytes\",\"type\":{\"kind\":7},\"optional\":false},{\"kind\":20,\"name\":\"maxBufferedBytes\",\"type\":{\"kind\":7},\"optional\":false},{\"kind\":20,\"name\":\"maxPeers\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"maxInboundPeers\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"maxOutboundPeers\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"maxCachedUrls\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"maxEndpointPins\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"endpointPins\",\"type\":{\"kind\":12,\"types\":[{\"kind\":16,\"typeName\":\"Readonly\",\"classType\":{\"$tsf\":2},\"typeArguments\":[{\"kind\":18,\"typeName\":\"Record\",\"utilityType\":\"Record\",\"typeArguments\":[{\"kind\":6},{\"kind\":6}],\"index\":{\"kind\":6},\"types\":[]}]},{\"kind\":4}]},\"optional\":true}]},\"optional\":false,\"default\":false},{\"name\":\"onClose\",\"type\":{\"$tsfType\":3},\"optional\":true,\"default\":false}]}]", [() => MeshLinkRuntime, require, () => (typeof Readonly !== "undefined" ? Readonly : (typeof exports !== "undefined" ? exports.Readonly : undefined)), () => (typeof Uint8Array !== "undefined" ? Uint8Array : (typeof exports !== "undefined" ? exports.Uint8Array : undefined)), () => (typeof Buffer !== "undefined" ? Buffer : (typeof exports !== "undefined" ? exports.Buffer : undefined))], __tsf_metadata_type);
}
exports.MeshLinkRuntime = MeshLinkRuntime;
_MeshLinkRuntimeMetadata_1.classType = MeshLinkRuntime;
MeshLinkRuntime.__tsfType = _MeshLinkRuntimeMetadata_1;
function peerIdentity(peer) {
    return `${peer.processId}:${peer.endpointId}:${peer.publicKey}`;
}
function readIdentity(url) {
    return {
        processId: url.searchParams.get('processId') ?? '',
        endpointId: url.searchParams.get('endpointId') ?? undefined,
        audienceEndpointId: url.searchParams.get('audienceEndpointId') ?? undefined,
        endpointPublicKey: url.searchParams.get('endpointPublicKey') ?? undefined,
        endpointSignature: url.searchParams.get('endpointSignature') ?? undefined,
        timestamp: Number(url.searchParams.get('timestamp')),
        nonce: url.searchParams.get('nonce') ?? '',
        signature: url.searchParams.get('signature') ?? ''
    };
}
function readProtocolVersion(url) {
    const raw = url.searchParams.get('protocolVersion');
    if (raw === null)
        throw new types_1.SrpcMeshAuthenticationError('Missing sRPC mesh handshake protocol version');
    const version = Number(raw);
    if (version !== protocol_1.MeshLinkProtocolVersion) {
        throw new types_1.SrpcMeshAuthenticationError('Unsupported sRPC mesh handshake protocol version');
    }
    return version;
}
function meshUrlKey(url) {
    const key = new URL(url);
    for (const parameter of [
        'protocolVersion',
        'processId',
        'endpointId',
        'timestamp',
        'nonce',
        'signature',
        'audienceEndpointId',
        'endpointPublicKey',
        'endpointSignature'
    ]) {
        key.searchParams.delete(parameter);
    }
    key.searchParams.sort();
    return key.toString();
}
function readHandshakeIdentity(headers) {
    const identity = {
        processId: readHeader(headers, HandshakeIdentityHeaders.processId),
        endpointId: readHeader(headers, HandshakeIdentityHeaders.endpointId),
        requesterEndpointId: readHeader(headers, HandshakeIdentityHeaders.requesterEndpointId),
        requestNonce: readHeader(headers, HandshakeIdentityHeaders.requestNonce),
        endpointPublicKey: readHeader(headers, 'x-tsf-mesh-endpoint-public-key'),
        endpointSignature: readHeader(headers, 'x-tsf-mesh-endpoint-signature'),
        timestamp: Number(readHeader(headers, HandshakeIdentityHeaders.timestamp)),
        nonce: readHeader(headers, HandshakeIdentityHeaders.nonce),
        signature: readHeader(headers, HandshakeIdentityHeaders.signature)
    };
    if (!identity.processId || !identity.endpointId || !identity.nonce || !identity.signature || !Number.isFinite(identity.timestamp)) {
        throw new types_1.SrpcMeshAuthenticationError('sRPC mesh server did not provide a handshake identity');
    }
    return identity;
}
function readHeader(headers, name) {
    const value = headers[name];
    return typeof value === 'string' ? value : '';
}
async function waitForPeer(promise, deadlineAt, clientId) {
    const remaining = remainingRequestMs(deadlineAt, clientId);
    let timer;
    try {
        return await Promise.race([
            promise,
            new Promise((_, reject) => {
                timer = setTimeout(() => reject(new types_1.SrpcOwnerUnavailableError(clientId, new Error('sRPC mesh link connection timed out'))), remaining);
                timer.unref?.();
            })
        ]);
    }
    finally {
        if (timer)
            clearTimeout(timer);
    }
}
function remainingRequestMs(deadlineAt, clientId) {
    const remaining = Math.ceil(deadlineAt - Date.now());
    if (remaining < 1) {
        throw new types_1.SrpcOwnerUnavailableError(clientId, new Error('sRPC mesh link connection timed out'));
    }
    return Math.min(remaining, helpers_1.MAX_SAFE_TIMER_MS);
}
function validateRuntimeOptions(options) {
    if (!options.path.startsWith('/') || options.path.includes('?') || options.path.includes('#')) {
        throw new Error('sRPC mesh-link path must be an absolute URL path');
    }
    if (!Number.isSafeInteger(options.connectTimeoutMs) || options.connectTimeoutMs < 100 || options.connectTimeoutMs > helpers_1.MAX_SAFE_TIMER_MS) {
        throw new Error('sRPC mesh-link connect timeout must be between 100ms and the platform timer limit');
    }
    if (!Number.isSafeInteger(options.idleTimeoutMs) || options.idleTimeoutMs < 1000 || options.idleTimeoutMs > helpers_1.MAX_SAFE_TIMER_MS) {
        throw new Error('sRPC mesh-link idle timeout must be between 1000ms and the platform timer limit');
    }
    if (!Number.isSafeInteger(options.maxFrameBytes) || options.maxFrameBytes < 1024 || options.maxFrameBytes > 64 * 1024 * 1024) {
        throw new Error('sRPC mesh-link frame limit must be between 1KiB and 64MiB');
    }
    if (!Number.isSafeInteger(options.maxBufferedBytes) || options.maxBufferedBytes < options.maxFrameBytes) {
        throw new Error('sRPC mesh-link buffered-byte limit must be at least the frame limit');
    }
    for (const [name, value] of [
        ['maxPeers', options.maxPeers],
        ['maxInboundPeers', options.maxInboundPeers],
        ['maxOutboundPeers', options.maxOutboundPeers],
        ['maxCachedUrls', options.maxCachedUrls],
        ['maxEndpointPins', options.maxEndpointPins]
    ]) {
        if (value !== undefined && (!Number.isSafeInteger(value) || value < 1))
            throw new Error(`sRPC mesh-link ${name} must be a positive integer`);
    }
    const maxPeers = options.maxPeers ?? 1024;
    const maxInbound = options.maxInboundPeers ?? Math.max(1, Math.floor(maxPeers / 2));
    const maxOutbound = options.maxOutboundPeers ?? maxPeers - maxInbound;
    if (maxPeers < 2 || maxInbound + maxOutbound > maxPeers) {
        throw new Error('sRPC mesh-link peer limits require at least two total slots and inbound+outbound no greater than total');
    }
}
function normalizedResourceLimits(options) {
    const maxPeers = options.maxPeers ?? 1024;
    const maxInboundPeers = options.maxInboundPeers ?? Math.max(1, Math.floor(maxPeers / 2));
    return JSON.stringify({
        maxPeers,
        maxInboundPeers,
        maxOutboundPeers: options.maxOutboundPeers ?? maxPeers - maxInboundPeers,
        maxCachedUrls: options.maxCachedUrls ?? maxPeers,
        maxEndpointPins: options.maxEndpointPins ?? maxPeers * 2
    });
}
function normalizedPins(pins) {
    return JSON.stringify(Object.entries(pins ?? {}).sort(([left], [right]) => left.localeCompare(right)));
}
function withTimeout(promise, timeoutMs, message) {
    return new Promise((resolve, reject) => {
        const timer = setTimeout(() => reject(new types_1.SrpcMeshAuthenticationError(message)), timeoutMs);
        timer.unref?.();
        promise.then(value => {
            clearTimeout(timer);
            resolve(value);
        }, error => {
            clearTimeout(timer);
            reject(error);
        });
    });
}
exports.__tsfTypeAliases = _a.decodeCompactMetadataV1("[1,{\"MeshLinkEndpointPinResolver\":{\"kind\":2,\"typeName\":\"(processId: string, endpointId: string) =\\u003e Promise\\u003cstring | MeshLinkResolvedEndpointPin | undefined\\u003e\",\"typeName\":\"MeshLinkEndpointPinResolver\"},\"MeshLinkResolvedEndpointPin\":{\"kind\":18,\"typeName\":\"MeshLinkResolvedEndpointPin\",\"types\":[{\"kind\":20,\"name\":\"publicKey\",\"type\":{\"kind\":6},\"optional\":false},{\"kind\":20,\"name\":\"expiresAt\",\"type\":{\"kind\":7},\"optional\":false}]},\"MeshLinkRuntimeOptions\":{\"kind\":18,\"typeName\":\"MeshLinkRuntimeOptions\",\"types\":[{\"kind\":20,\"name\":\"path\",\"type\":{\"kind\":6},\"optional\":false},{\"kind\":20,\"name\":\"secret\",\"type\":{\"kind\":6},\"optional\":false},{\"kind\":20,\"name\":\"httpServer\",\"type\":{\"kind\":12,\"types\":[{\"$tsfAlias\":[0,\"node:http\",\"Server\",\"Server\"]},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"connectTimeoutMs\",\"type\":{\"kind\":7},\"optional\":false},{\"kind\":20,\"name\":\"idleTimeoutMs\",\"type\":{\"kind\":7},\"optional\":false},{\"kind\":20,\"name\":\"maxFrameBytes\",\"type\":{\"kind\":7},\"optional\":false},{\"kind\":20,\"name\":\"maxBufferedBytes\",\"type\":{\"kind\":7},\"optional\":false},{\"kind\":20,\"name\":\"maxPeers\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"maxInboundPeers\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"maxOutboundPeers\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"maxCachedUrls\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"maxEndpointPins\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"endpointPins\",\"type\":{\"kind\":12,\"types\":[{\"kind\":16,\"typeName\":\"Readonly\",\"classType\":{\"$tsf\":1},\"typeArguments\":[{\"kind\":18,\"typeName\":\"Record\",\"utilityType\":\"Record\",\"typeArguments\":[{\"kind\":6},{\"kind\":6}],\"index\":{\"kind\":6},\"types\":[]}]},{\"kind\":4}]},\"optional\":true}]}}]", [require, () => (typeof Readonly !== "undefined" ? Readonly : (typeof exports !== "undefined" ? exports.Readonly : undefined))], __tsf_metadata_type);
//# sourceMappingURL=runtime.js.map