import {z} from 'zod' export type GetApiShipmentFormatSearatesArgs = {shipmentNumber: string, sealine?: string | undefined, shipmentType?: ('CT' | 'BL' | 'BK') | undefined, route?: boolean | undefined, ais?: boolean | undefined, safecubeFields?: boolean | undefined}; export type GetApiShipmentFormatSearatesOptions = {baseUrl?: string | undefined, ApiKey?: string | undefined, fetch?: typeof fetch | undefined}; export const searatesRequestStatus = z.enum(["success", "error"]); export const searatesShippingStatus = z.enum(["PLANNED", "IN_TRANSIT", "DELIVERED", "UNKNOWN", "CANCELLED"]); export const searatesApiCalls = z.object({total: z.number().int().nullable().optional(), used: z.number().int().nullable().optional(), remaining: z.number().int().nullable().optional()}); export const searatesUniqueShipments = z.object({total: z.number().int().nullable().optional(), used: z.number().int().nullable().optional(), remaining: z.number().int().nullable().optional()}); export const searatesMetadata = z.object({type: z.enum(["CT", "BL", "BK"]).optional(), number: z.string().optional(), sealine: z.string().nullable().optional(), sealine_name: z.string().nullable().optional(), status: searatesShippingStatus.optional(), updated_at: z.string().nullable().optional(), api_calls: searatesApiCalls.optional(), unique_shipments: searatesUniqueShipments.optional(), warnings: z.array(z.enum(["OBSOLETE_DATA", "NO_POL", "NO_POD", "MISSING_DATE", "STATUS_MISMATCH", "NO_EVENTS", "NO_CONTAINERS", "SPLIT_BL"])).nullable().optional()}); export const searatesLocation = z.object({id: z.number().int().optional(), name: z.string().optional(), state: z.string().nullable().optional(), country: z.string().nullable().optional(), country_code: z.string().nullable().optional(), locode: z.string().nullable().optional(), lat: z.number().nullable().optional(), lng: z.number().nullable().optional(), timezone: z.string().nullable().optional()}); export const searatesRoutePoint = z.object({location: z.number().int().nullable().optional(), date: z.string().nullable().optional(), actual: z.boolean().nullable().optional()}); export const searatesRoutePod = z.object({location: z.number().int().nullable().optional(), date: z.string().nullable().optional(), actual: z.boolean().nullable().optional(), predictive_eta: z.string().nullable().optional()}); export const searatesRoute = z.object({prepol: searatesRoutePoint.optional(), pol: searatesRoutePoint.optional(), pod: searatesRoutePod.optional(), postpod: searatesRoutePoint.optional()}); export const searatesVessel = z.object({id: z.number().int().optional(), name: z.string().optional(), imo: z.number().int().nullable().optional(), call_sign: z.string().nullable().optional(), mmsi: z.number().int().nullable().optional(), flag: z.string().nullable().optional()}); export const searatesEvent = z.object({order_id: z.number().int().optional(), location: z.number().int().nullable().optional(), facility: z.number().int().nullable().optional(), description: z.string().optional(), event_type: z.enum(["SHIPMENT", "TRANSPORT", "EQUIPMENT"]).nullable().optional(), event_code: z.enum(["ARRI", "DEPA", "LOAD", "DISC", "GTIN", "GTOT", "STUF", "STRP", "PICK", "AVPU", "DROP", "AVDO", "INSP", "RSEA", "RMVD", "CUSS", "CUSI", "CUSR", "CROS", "RECE", "DRFT", "PENA", "PENU", "PENC", "CONF", "REJE", "APPR", "ISSU", "SURR", "SUBM", "VOID", "REQS", "CMPL", "HOLD", "RELS", "CANC"]).nullable().optional(), status: z.enum(["CEP", "CPS", "CGI", "CLL", "VDL", "VAT", "CDT", "TSD", "CLT", "VDT", "VAD", "CDD", "CGO", "CDC", "CER", "LTS", "BTS", "UNK"]).optional(), date: z.string().optional(), actual: z.boolean().nullable().optional(), is_additional_event: z.boolean().optional(), type: z.enum(["sea", "land"]).optional(), transport_type: z.enum(["VESSEL", "BARGE", "FEEDER", "TRUCK", "RAIL", "AIR"]).nullable().optional(), vessel: z.number().int().nullable().optional(), voyage: z.string().nullable().optional()}); export const searatesContainer = z.object({number: z.string().optional(), iso_code: z.string().nullable().optional(), size_type: z.string().nullable().optional(), status: searatesShippingStatus.optional(), events: z.array(searatesEvent).nullable().optional()}); export const searatesFacility = z.object({id: z.number().int().optional(), name: z.string().optional(), country_code: z.string().nullable().optional(), locode: z.string().nullable().optional(), bic_code: z.string().nullable().optional(), smdg_code: z.string().nullable().optional(), lat: z.number().nullable().optional(), lng: z.number().nullable().optional()}); export const searatesRouteItem = z.object({path: z.array(z.array(z.number())).optional(), type: z.enum(["SEA", "LAND"]).optional()}); export const searatesAisLastEvent = z.object({description: z.string().optional(), date: z.string().optional(), voyage: z.string().optional()}); export const searatesAisDischargePort = z.object({name: z.string().nullable().optional(), country_code: z.string().nullable().optional(), code: z.string().nullable().optional(), date: z.string().nullable().optional(), date_label: z.enum(["ETA", "ATA"]).nullable().optional()}); export const searatesAisVessel = z.object({name: z.string().optional(), imo: z.number().int().nullable().optional(), call_sign: z.string().nullable().optional(), mmsi: z.number().int().nullable().optional(), flag: z.string().nullable().optional()}); export const searatesAisVesselPosition = z.object({lat: z.number().nullable().optional(), lng: z.number().nullable().optional(), updated_at: z.string().nullable().optional()}); export const searatesAisDeparturePort = z.object({country_code: z.string().nullable().optional(), code: z.string().nullable().optional(), date: z.string().nullable().optional(), date_label: z.enum(["ETD", "ATD"]).nullable().optional()}); export const searatesAisArrivalPort = z.object({country_code: z.string().nullable().optional(), code: z.string().nullable().optional(), date: z.string().nullable().optional(), date_label: z.enum(["ETA", "ATA"]).nullable().optional()}); export const searatesAisData = z.object({last_event: searatesAisLastEvent.optional(), discharge_port: searatesAisDischargePort.optional(), vessel: searatesAisVessel.optional(), last_vessel_position: searatesAisVesselPosition.optional(), departure_port: searatesAisDeparturePort.optional(), arrival_port: searatesAisArrivalPort.optional(), updated_at: z.string().nullable().optional()}).nullable(); export const searatesAis = z.object({status: z.enum(["OK", "NOT_ON_BOARD", "NO_AIS_DATA"]).optional(), data: searatesAisData.optional()}); export const searatesRouteData = z.object({route: z.array(searatesRouteItem).nullable().optional(), pin: z.array(z.number()).nullable().optional(), ais: searatesAis.optional()}).nullable(); export const searatesShipmentData = z.object({metadata: searatesMetadata.optional(), locations: z.array(searatesLocation).nullable().optional(), route: searatesRoute.optional(), vessels: z.array(searatesVessel).nullable().optional(), containers: z.array(searatesContainer).nullable().optional(), facilities: z.array(searatesFacility).nullable().optional(), route_data: searatesRouteData.optional()}); export const searatesShipmentFormat = z.object({status: searatesRequestStatus.optional(), message: z.enum(["OK", "SEALINE_HASNT_PROVIDE_INFO", "NO_CONTAINERS", "NO_EVENTS", "WRONG_PARAMETERS", "WRONG_NUMBER", "WRONG_SEALINE", "WRONG_TYPE", "SEALINE_NO_RESPONSE", "SEALINE_NOT_SUPPORTED", "SEALINE_TEMPORARY_DISABLED", "SEALINE_UNDER_MAINTENANCE", "AUTO_CANT_DETECT_SEALINE", "AUTO_CANT_DETECT_TYPE", "AUTO_CANT_FIND_INFO", "SEALINE_CANCELED_SHIPMENT", "UNEXPECTED_ERROR"]).optional(), data: searatesShipmentData.optional()}); 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 }; /** * Retrieve shipment tracking data in a Searates-compatible format * * GET /shipment-format/searates */ export const getApiShipmentFormatSearates = async (args: GetApiShipmentFormatSearatesArgs, options?: GetApiShipmentFormatSearatesOptions) => { const {baseUrl = "https://api.sinay.ai/container-tracking/api/v2", fetch: fetchFn = fetch, ApiKey} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('accept', "application/json") if (args.shipmentNumber != null) query.append("shipmentNumber", String(args.shipmentNumber)) if (args.sealine != null) query.append("sealine", String(args.sealine)) if (args.shipmentType != null) query.append("shipmentType", String(args.shipmentType)) if (args.route != null) query.append("route", String(args.route)) if (args.ais != null) query.append("ais", String(args.ais)) if (args.safecubeFields != null) query.append("safecubeFields", String(args.safecubeFields)) if (ApiKey !== undefined) headers.set("API_KEY", ApiKey) const url = `${baseUrl.replace(/\/+$/, '')}/shipment-format/searates${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "GET", headers}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(searatesShipmentFormat, await res.json()) };