import { z } from 'zod'; import { ExternalProxyEntrySchema } from './external-proxy'; import { FinalMaskStreamSettingsSchema } from './finalmask'; import { GrpcStreamSettingsSchema } from './grpc'; import { HttpUpgradeStreamSettingsSchema } from './httpupgrade'; import { HysteriaStreamSettingsSchema } from './hysteria'; import { KcpStreamSettingsSchema } from './kcp'; import { SockoptStreamSettingsSchema } from './sockopt'; import { TcpStreamSettingsSchema } from './tcp'; import { WsStreamSettingsSchema } from './ws'; import { XHttpStreamSettingsSchema } from './xhttp'; export * from './external-proxy'; export * from './finalmask'; export * from './grpc'; export * from './httpupgrade'; export * from './hysteria'; export * from './kcp'; export * from './sockopt'; export * from './tcp'; export * from './ws'; export * from './xhttp'; export const NetworkSchema = z.enum([ 'tcp', 'kcp', 'ws', 'grpc', 'httpupgrade', 'xhttp', 'hysteria', ]); export type Network = z.infer; // Tagged-wrapper DU on `network`. The wire shape uses an asymmetric per- // network key (`tcpSettings`, `wsSettings`, ...) rather than a single // `settings` object — same pattern Xray ships and the panel's StreamSettings // class flattens via toJson. Each branch carries only the matching key so // fixtures round-trip byte-identical. // // `hysteria` is only valid when the parent protocol is hysteria — the // network selector hides it for other protocols. xray-core enforces // the constraint server-side too. export const NetworkSettingsSchema = z.discriminatedUnion('network', [ z.object({ network: z.literal('tcp'), tcpSettings: TcpStreamSettingsSchema }), z.object({ network: z.literal('kcp'), kcpSettings: KcpStreamSettingsSchema }), z.object({ network: z.literal('ws'), wsSettings: WsStreamSettingsSchema }), z.object({ network: z.literal('grpc'), grpcSettings: GrpcStreamSettingsSchema }), z.object({ network: z.literal('httpupgrade'), httpupgradeSettings: HttpUpgradeStreamSettingsSchema }), z.object({ network: z.literal('xhttp'), xhttpSettings: XHttpStreamSettingsSchema }), z.object({ network: z.literal('hysteria'), hysteriaSettings: HysteriaStreamSettingsSchema }), ]); export type NetworkSettings = z.infer; // Orthogonal extras that ride alongside the network and security branches. // All optional on the wire — legacy toJson() omits any field whose value // is empty. The shadow harness treats absent and empty-array as the same // canonical state. export const StreamExtrasSchema = z.object({ externalProxy: z.array(ExternalProxyEntrySchema).optional(), finalmask: FinalMaskStreamSettingsSchema.optional(), sockopt: SockoptStreamSettingsSchema.optional(), }); export type StreamExtras = z.infer;