import {z} from 'zod' export type Curve = {begin: number, end: number, radius: number}; export type TrackSectionSncfExtension = {line_code: number, line_name: string, track_name: string, track_number: number}; export type TrackSectionExtensions = {sncf?: (TrackSectionSncfExtension | null) | undefined, source?: ({id: string, name: string} | null) | undefined}; export type GeoJsonPointValue = Array; export type GeoJsonLineStringValue = Array; export type GeoJsonLineString = {coordinates: GeoJsonLineStringValue, type: 'LineString'}; export type LoadingGaugeType = 'G1' | 'G2' | 'GA' | 'GB' | 'GB1' | 'GC' | 'FR3.3' | 'FR3.3/GB/G2' | 'GLOTT'; export type LoadingGaugeLimit = {begin: number, category: LoadingGaugeType, end: number}; export type Slope = {begin: number, end: number, gradient: number}; export type TrackSection = {curves: Array, extensions?: TrackSectionExtensions | undefined, geo: GeoJsonLineString, id: string, length: number, loading_gauge_limits?: Array | undefined, slopes: Array}; export type Direction = 'START_TO_STOP' | 'STOP_TO_START'; export type Side = 'LEFT' | 'RIGHT' | 'CENTER'; export type SignalSncfExtension = {kp: string, label: string, side?: Side | undefined}; export type SignalExtensions = {sncf?: (SignalSncfExtension | null) | undefined}; export type Signal = {direction: Direction, extensions?: SignalExtensions | undefined, id: string, logical_signals?: Array<{conditional_parameters: Array<{on_route: string, parameters: {[key: string]: string}}>, default_parameters: {[key: string]: string}, next_signaling_systems: Array, settings: {[key: string]: string}, signaling_system: string}> | undefined, position: number, sight_distance: number, track: string}; export type DirectionalTrackRange = {begin: number, direction: Direction, end: number, track: string}; export type Sign = {direction: Direction, kp: string, position: number, side: Side, track: string, type: string, value: string}; export type NeutralSectionNeutralSncfExtension = {announcement: Array, end: Array, exe: Sign, rev: Array}; export type NeutralSectionExtensions = {neutral_sncf?: (NeutralSectionNeutralSncfExtension | null) | undefined}; export type NeutralSection = {announcement_track_ranges: Array, extensions?: NeutralSectionExtensions | undefined, id: string, lower_pantograph: boolean, track_ranges: Array}; export type SpeedSectionPslSncfExtension = {announcement: Array, r: Array, z: Sign}; export type SpeedSectionExtensions = {psl_sncf?: (SpeedSectionPslSncfExtension | null) | undefined}; export type ApplicableDirections = 'START_TO_STOP' | 'STOP_TO_START' | 'BOTH'; export type ApplicableDirectionsTrackRange = {applicable_directions: ApplicableDirections, begin: number, end: number, track: string}; export type SpeedSection = {extensions?: SpeedSectionExtensions | undefined, id: string, on_routes?: (Array | null) | undefined, speed_limit?: (number | null) | undefined, speed_limit_by_tag: {[key: string]: number}, track_ranges: Array}; export type SwitchSncfExtension = {label: string}; export type SwitchExtensions = {sncf?: (SwitchSncfExtension | null) | undefined}; export type Endpoint = 'BEGIN' | 'END'; export type TrackEndpoint = {endpoint: Endpoint, track: string}; export type Switch = {extensions?: SwitchExtensions | undefined, group_change_delay: number, id: string, ports: {[key: string]: TrackEndpoint}, switch_type: string}; export type SwitchPortConnection = {dst: string, src: string}; export type SwitchType = {groups: {[key: string]: Array}, id: string, ports: Array}; export type DetectorSncfExtension = {kp: string}; export type DetectorExtension = {sncf: DetectorSncfExtension}; export type Detector = {extensions?: DetectorExtension | undefined, id: string, position: number, track: string}; export type BufferStopSncfExtension = {kp: string}; export type BufferStopExtension = {sncf?: (BufferStopSncfExtension | null) | undefined}; export type BufferStop = {extensions?: BufferStopExtension | undefined, id: string, position: number, track: string}; export type Waypoint = {id: string, type: 'BufferStop'} | {id: string, type: 'Detector'}; export type Route = {entry_point: Waypoint, entry_point_direction: Direction, exit_point: Waypoint, id: string, release_detectors: Array, switches_directions: {[key: string]: string}}; export type OperationalPointPartSncfExtension = {kp: string}; export type OperationalPointPartExtension = {sncf?: (OperationalPointPartSncfExtension | null) | undefined}; export type NonBlankString = string; export type OperationalPointPart = {extensions?: OperationalPointPartExtension | undefined, local_track_name: NonBlankString, position: number, track: string}; export type OperationalPoint = {country_code: string, id: string, is_passenger_station: boolean, main_code: string, name: string, parts: Array, plc?: (string | null) | undefined, secondary_code?: (NonBlankString | null) | undefined, secondary_name?: (string | null) | undefined, uic?: (number | null) | undefined, weight?: (number | null) | undefined}; export type Electrification = {id: string, track_ranges: Array, voltage: string}; export type LevelCrossingPart = {pedal_downstream: number, pedal_upstream: number, position: number, track: string}; export type LevelCrossing = {id: string, name: string, parts: Array, short_zone_length: number}; export type ObjectType = 'TrackSection' | 'Signal' | 'SpeedSection' | 'Detector' | 'NeutralSection' | 'Switch' | 'SwitchType' | 'BufferStop' | 'Route' | 'OperationalPoint' | 'Electrification' | 'LevelCrossing'; export type PatchOperation = {path: string, value: unknown, op: 'add'} | {path: string, op: 'remove'} | {path: string, value: unknown, op: 'replace'} | {from: string, path: string, op: 'move'} | {from: string, path: string, op: 'copy'} | {path: string, value: unknown, op: 'test'}; export type Operation = {obj_type: 'TrackSection', railjson: TrackSection, operation_type: 'CREATE'} | {obj_type: 'Signal', railjson: Signal, operation_type: 'CREATE'} | {obj_type: 'NeutralSection', railjson: NeutralSection, operation_type: 'CREATE'} | {obj_type: 'SpeedSection', railjson: SpeedSection, operation_type: 'CREATE'} | {obj_type: 'Switch', railjson: Switch, operation_type: 'CREATE'} | {obj_type: 'SwitchType', railjson: SwitchType, operation_type: 'CREATE'} | {obj_type: 'Detector', railjson: Detector, operation_type: 'CREATE'} | {obj_type: 'BufferStop', railjson: BufferStop, operation_type: 'CREATE'} | {obj_type: 'Route', railjson: Route, operation_type: 'CREATE'} | {obj_type: 'OperationalPoint', railjson: OperationalPoint, operation_type: 'CREATE'} | {obj_type: 'Electrification', railjson: Electrification, operation_type: 'CREATE'} | {obj_type: 'LevelCrossing', railjson: LevelCrossing, operation_type: 'CREATE'} | {obj_id: string, obj_type: ObjectType, railjson_patch: Array, operation_type: 'UPDATE'} | {obj_id: string, obj_type: ObjectType, operation_type: 'DELETE'}; export type CreateApiInfraInfraIdArgs = {infra_id: number, body: Array}; export type CreateApiInfraInfraIdOptions = {baseUrl?: string | undefined, fetch?: typeof fetch | undefined}; export const curve = z.object({begin: z.number(), end: z.number(), radius: z.number()}); export const trackSectionSncfExtension = z.object({line_code: z.number().int(), line_name: z.string().min(1), track_name: z.string().min(1), track_number: z.number().int()}); export const trackSectionExtensions = z.object({sncf: trackSectionSncfExtension.nullable().optional(), source: z.object({id: z.string().min(1), name: z.string().min(1)}).nullable().optional()}); export const geoJsonPointValue = z.array(z.number()); export const geoJsonLineStringValue = z.array(geoJsonPointValue); export const geoJsonLineString = z.object({coordinates: geoJsonLineStringValue, type: z.literal("LineString")}); export const loadingGaugeType = z.enum(["G1", "G2", "GA", "GB", "GB1", "GC", "FR3.3", "FR3.3/GB/G2", "GLOTT"]); export const loadingGaugeLimit = z.object({begin: z.number(), category: loadingGaugeType, end: z.number()}); export const slope = z.object({begin: z.number(), end: z.number(), gradient: z.number()}); export const trackSection = z.object({curves: z.array(curve), extensions: trackSectionExtensions.optional(), geo: geoJsonLineString, id: z.string().min(1).max(255), length: z.number(), loading_gauge_limits: z.array(loadingGaugeLimit).optional(), slopes: z.array(slope)}); export const direction = z.enum(["START_TO_STOP", "STOP_TO_START"]); export const side = z.enum(["LEFT", "RIGHT", "CENTER"]); export const signalSncfExtension = z.object({kp: z.string(), label: z.string(), side: side.optional()}); export const signalExtensions = z.object({sncf: signalSncfExtension.nullable().optional()}); export const signal = z.object({direction: direction, extensions: signalExtensions.optional(), id: z.string().min(1).max(255), logical_signals: z.array(z.object({conditional_parameters: z.array(z.object({on_route: z.string().min(1), parameters: z.record(z.string(), z.string().min(1))})), default_parameters: z.record(z.string(), z.string().min(1)), next_signaling_systems: z.array(z.string()), settings: z.record(z.string(), z.string().min(1)), signaling_system: z.string()})).optional(), position: z.number(), sight_distance: z.number(), track: z.string().min(1).max(255)}); export const directionalTrackRange = z.object({begin: z.number(), direction: direction, end: z.number(), track: z.string().min(1).max(255)}); export const sign = z.object({direction: direction, kp: z.string(), position: z.number(), side: side, track: z.string().min(1).max(255), type: z.string().min(1), value: z.string()}); export const neutralSectionNeutralSncfExtension = z.object({announcement: z.array(sign), end: z.array(sign), exe: sign, rev: z.array(sign)}); export const neutralSectionExtensions = z.object({neutral_sncf: neutralSectionNeutralSncfExtension.nullable().optional()}); export const neutralSection = z.object({announcement_track_ranges: z.array(directionalTrackRange), extensions: neutralSectionExtensions.optional(), id: z.string().min(1).max(255), lower_pantograph: z.boolean(), track_ranges: z.array(directionalTrackRange)}); export const speedSectionPslSncfExtension = z.object({announcement: z.array(sign), r: z.array(sign), z: sign}); export const speedSectionExtensions = z.object({psl_sncf: speedSectionPslSncfExtension.nullable().optional()}); export const applicableDirections = z.enum(["START_TO_STOP", "STOP_TO_START", "BOTH"]); export const applicableDirectionsTrackRange = z.object({applicable_directions: applicableDirections, begin: z.number(), end: z.number(), track: z.string().min(1).max(255)}); export const speedSection = z.object({extensions: speedSectionExtensions.optional(), id: z.string().min(1).max(255), on_routes: z.array(z.string().min(1).max(255)).nullable().optional(), speed_limit: z.number().nullable().optional(), speed_limit_by_tag: z.record(z.string(), z.number()), track_ranges: z.array(applicableDirectionsTrackRange)}); export const switchSncfExtension = z.object({label: z.string().min(1)}); export const switchExtensions = z.object({sncf: switchSncfExtension.nullable().optional()}); export const endpoint = z.enum(["BEGIN", "END"]); export const trackEndpoint = z.object({endpoint: endpoint, track: z.string().min(1).max(255)}); export const switch = z.object({extensions: switchExtensions.optional(), group_change_delay: z.number(), id: z.string().min(1).max(255), ports: z.record(z.string(), trackEndpoint), switch_type: z.string().min(1).max(255)}); export const switchPortConnection = z.object({dst: z.string().min(1).max(255), src: z.string().min(1).max(255)}); export const switchType = z.object({groups: z.record(z.string(), z.array(switchPortConnection)), id: z.string().min(1).max(255), ports: z.array(z.string().min(1).max(255))}); export const detectorSncfExtension = z.object({kp: z.string()}); export const detectorExtension = z.object({sncf: detectorSncfExtension}); export const detector = z.object({extensions: detectorExtension.optional(), id: z.string().min(1).max(255), position: z.number(), track: z.string().min(1).max(255)}); export const bufferStopSncfExtension = z.object({kp: z.string()}); export const bufferStopExtension = z.object({sncf: bufferStopSncfExtension.nullable().optional()}); export const bufferStop = z.object({extensions: bufferStopExtension.optional(), id: z.string().min(1).max(255), position: z.number(), track: z.string().min(1).max(255)}); export const waypoint = z.union([z.object({id: z.string().min(1).max(255), type: z.literal("BufferStop")}), z.object({id: z.string().min(1).max(255), type: z.literal("Detector")})]); export const route = z.object({entry_point: waypoint, entry_point_direction: direction, exit_point: waypoint, id: z.string().min(1).max(255), release_detectors: z.array(z.string().min(1).max(255)), switches_directions: z.record(z.string(), z.string().min(1).max(255))}); export const operationalPointPartSncfExtension = z.object({kp: z.string()}); export const operationalPointPartExtension = z.object({sncf: operationalPointPartSncfExtension.nullable().optional()}); export const nonBlankString = z.string().min(1); export const operationalPointPart = z.object({extensions: operationalPointPartExtension.optional(), local_track_name: nonBlankString, position: z.number(), track: z.string().min(1).max(255)}); export const operationalPoint = z.object({country_code: z.string().min(1), id: z.string().min(1).max(255), is_passenger_station: z.boolean(), main_code: z.string().min(1), name: z.string().min(1), parts: z.array(operationalPointPart), plc: z.string().min(1).nullable().optional(), secondary_code: nonBlankString.nullable().optional(), secondary_name: z.string().min(1).nullable().optional(), uic: z.number().int().gte(0).nullable().optional(), weight: z.number().int().gte(0).nullable().optional()}); export const electrification = z.object({id: z.string().min(1).max(255), track_ranges: z.array(applicableDirectionsTrackRange), voltage: z.string().min(1)}); export const levelCrossingPart = z.object({pedal_downstream: z.number().int().gte(0), pedal_upstream: z.number().int().gte(0), position: z.number(), track: z.string().min(1).max(255)}); export const levelCrossing = z.object({id: z.string().min(1).max(255), name: z.string(), parts: z.array(levelCrossingPart), short_zone_length: z.number().int().gte(0)}); export const infraObject = z.union([z.object({obj_type: z.literal("TrackSection"), railjson: trackSection}), z.object({obj_type: z.literal("Signal"), railjson: signal}), z.object({obj_type: z.literal("NeutralSection"), railjson: neutralSection}), z.object({obj_type: z.literal("SpeedSection"), railjson: speedSection}), z.object({obj_type: z.literal("Switch"), railjson: switch}), z.object({obj_type: z.literal("SwitchType"), railjson: switchType}), z.object({obj_type: z.literal("Detector"), railjson: detector}), z.object({obj_type: z.literal("BufferStop"), railjson: bufferStop}), z.object({obj_type: z.literal("Route"), railjson: route}), z.object({obj_type: z.literal("OperationalPoint"), railjson: operationalPoint}), z.object({obj_type: z.literal("Electrification"), railjson: electrification}), z.object({obj_type: z.literal("LevelCrossing"), railjson: levelCrossing})]); export const createApiInfraInfraIdResponse = z.array(infraObject); export const validateResponse = async ( schema: { readonly '~standard': { readonly validate: (value: unknown) => ({ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }) | Promise<{ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }> } }, value: unknown ): Promise => { const result = await schema['~standard'].validate(value) if (result.issues !== undefined) { const detail = result.issues .map(({ message, path }) => path?.length ? `${path.map((segment) => String(typeof segment === 'object' ? segment.key : segment)).join('.')}: ${message}` : message ) .join('; ') throw new Error(`Response does not match the schema: ${detail}`, { cause: { issues: result.issues } }) } return result.value }; /** * Edit the content of an infrastructure * * POST /infra/{infra_id} */ export const createApiInfraInfraId = async (args: CreateApiInfraInfraIdArgs, options?: CreateApiInfraInfraIdOptions) => { const {baseUrl = "https://demo.osrd.fr/api", fetch: fetchFn = fetch} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('content-type', "application/json") headers.set('accept', "application/json") const url = `${baseUrl.replace(/\/+$/, '')}/infra/${encodeURIComponent(String(args.infra_id))}${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "POST", headers, body: JSON.stringify(args.body)}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(createApiInfraInfraIdResponse, await res.json()) };