/**
 * syncRequestResponse — request/response protocol for WARP sync.
 *
 * Handles creation and processing of SyncRequest/SyncResponse messages,
 * plus the client-side application of received patches.
 *
 * @module domain/services/sync/syncRequestResponse
 * @see SyncProtocol — WARP sync spec Section 11 (Network Sync)
 * @see JoinReducer — CRDT merge implementation
 */
import { cloneState } from '../JoinReducer.ts';
import { type DecodedPatch } from './syncPatchLoader.ts';
import { type CreateSyncRequestOptions, type SyncRequestPage, type SyncResponseMetrics, type SyncResponsePage } from './SyncResponsePaging.ts';
import type WarpState from '../state/WarpState.ts';
import type CommitPort from '../../../ports/CommitPort.ts';
import type BlobPort from '../../../ports/BlobPort.ts';
import type PatchJournalPort from '../../../ports/PatchJournalPort.ts';
import type LoggerPort from '../../../ports/LoggerPort.ts';
export type { CreateSyncRequestOptions, SyncRequestPage, SyncResponseMetrics, SyncResponsePage, } from './SyncResponsePaging.ts';
/**
 * A sync request message sent from one node to another.
 *
 * The requester sends its current frontier, allowing the responder to
 * compute what patches the requester is missing.
 */
export interface SyncRequest {
    /** Message type discriminator for protocol parsing */
    type: 'sync-request';
    /**
     * Requester's frontier as a plain object.
     * Keys are writer IDs, values are the SHA of each writer's latest known patch.
     * Converted from Map for JSON serialization.
     */
    frontier: Record<string, string>;
    /** Optional page budget for bounded sync responses. */
    page?: SyncRequestPage;
}
/** A patch entry in a sync response. */
export interface SyncPatchEntry {
    writerId: string;
    sha: string;
    patch: DecodedPatch;
}
/** A writer that was skipped during sync (e.g. divergence or trust gate). */
export interface SkippedWriterEntry {
    writerId: string;
    reason: string;
    localSha: string;
    remoteSha: string | null;
}
/**
 * A sync response message containing patches the requester needs.
 *
 * The responder includes its own frontier (so the requester knows what
 * the responder is missing) and the patches the requester needs to catch up.
 */
export interface SyncResponse {
    /** Message type discriminator for protocol parsing */
    type: 'sync-response';
    /**
     * Responder's frontier as a plain object.
     * Keys are writer IDs, values are SHAs.
     */
    frontier: Record<string, string>;
    /** Patches the requester needs, in chronological order per writer */
    patches: SyncPatchEntry[];
    /** Writers that were skipped during sync */
    skippedWriters?: SkippedWriterEntry[];
    /** Page metadata for bounded responses */
    page?: SyncResponsePage;
    /** Response-shaping metrics */
    metrics?: SyncResponseMetrics;
}
export interface ProcessSyncRequestOptions {
    patchJournal?: PatchJournalPort;
    logger?: LoggerPort;
    observedLatencyMs?: number;
}
export interface ApplySyncResponseResult {
    state: WarpState;
    frontier: Map<string, string>;
    applied: number;
}
/**
 * Creates a sync request message.
 *
 * Converts the frontier Map to a plain object for JSON serialization.
 * The resulting message can be sent over HTTP, WebSocket, or any other
 * transport that supports JSON.
 *
 * @param frontier - Local frontier mapping writer IDs to their latest known patch SHAs
 * @returns A sync request message ready for serialization
 *
 * @example
 * const frontier = new Map([['w1', 'sha-a'], ['w2', 'sha-b']]);
 * const request = createSyncRequest(frontier);
 * // { type: 'sync-request', frontier: { w1: 'sha-a', w2: 'sha-b' } }
 */
export declare function createSyncRequest(frontier: Map<string, string>, options?: CreateSyncRequestOptions): SyncRequest;
/**
 * Processes a sync request and returns patches the requester needs.
 *
 * This is the server-side handler for sync requests. It:
 * 1. Converts the incoming frontier from plain object to Map
 * 2. Computes what the requester is missing (using computeSyncDelta)
 * 3. Loads the missing patches from storage
 * 4. Returns a response with the local frontier and patches
 *
 * **Error handling**: If divergence is detected for a writer (their chain
 * has forked), that writer is silently skipped. The requester will not
 * receive patches for that writer and may need to handle this separately.
 *
 * @param request - Incoming sync request containing the requester's frontier
 * @param localFrontier - Local frontier (what this node has)
 * @param persistence - Git persistence layer for loading patches
 * @param graphName - Graph name for error messages and logging
 * @param options - Options including optional patchJournal and logger
 * @returns Response containing local frontier and patches
 * @throws {Error} If patch loading fails for reasons other than divergence
 *
 * @example
 * // Server-side sync handler
 * app.post('/sync', async (req, res) => {
 *   const request = req.body;
 *   const response = await processSyncRequest(request, localFrontier, persistence, 'events');
 *   res.json(response);
 * });
 */
export declare function processSyncRequest(request: SyncRequest, localFrontier: Map<string, string>, persistence: CommitPort & BlobPort, graphName: string, { patchJournal, logger, observedLatencyMs }?: ProcessSyncRequestOptions): Promise<SyncResponse>;
/**
 * Applies a sync response to local state.
 *
 * This is the client-side handler for sync responses. It:
 * 1. Clones state and frontier to avoid mutating inputs
 * 2. Groups patches by writer for correct ordering
 * 3. Validates each patch against known op types (schema compatibility)
 * 4. Applies patches using CRDT merge semantics (JoinReducer.join)
 * 5. Updates the frontier with new writer tips
 *
 * **CRDT convergence**: Patches can be applied in any order and the final
 * state will be identical. However, applying in chronological order (as
 * provided) is slightly more efficient.
 *
 * **Schema validation**: Patches are checked against EDGE_PROPERTY_PATCH_SCHEMA_VERSION before apply.
 * If a patch contains op types we don't understand (from a newer schema),
 * assertOpsCompatible throws to prevent silent data loss.
 *
 * **Immutability**: This function does not modify the input state or frontier.
 * It returns new objects.
 *
 * @param response - Incoming sync response containing patches
 * @param state - Current CRDT state
 * @param frontier - Current frontier mapping writer IDs to SHAs
 * @returns New state, frontier, and count of applied patches
 * @throws {SchemaUnsupportedError} If a patch contains unsupported op types
 *
 * @example
 * const response = await fetch('/sync', { ... }).then(r => r.json());
 * const result = applySyncResponse(response, currentState, currentFrontier);
 * console.log(`Applied ${result.applied} patches`);
 * currentState = result.state;
 * currentFrontier = result.frontier;
 */
export declare function applySyncResponse(response: SyncResponse, state: Parameters<typeof cloneState>[0], frontier: Map<string, string>): ApplySyncResponseResult;
/**
 * Creates an empty sync response (used when no patches are needed).
 *
 * This is a convenience function for responding to sync requests when
 * the requester is already up-to-date (or ahead). The response includes
 * the local frontier but no patches.
 *
 * @param frontier - Local frontier to include in the response
 * @returns A sync response with empty patches array
 *
 * @example
 * if (!syncNeeded(remoteFrontier, localFrontier)) {
 *   return createEmptySyncResponse(localFrontier);
 * }
 */
export declare function createEmptySyncResponse(frontier: Map<string, string>): SyncResponse;
