import {z} from 'zod' export type TransportMode = 'parcel' | 'postal' | 'express' | 'road_freight' | 'air_cargo' | 'ocean_container'; export type DeliveryCompletionScope = 'carrier_acceptance_to_delivery' | 'airport_to_airport' | 'airport_to_availability' | 'port_to_port' | 'port_to_availability' | 'port_to_gate_out'; export type DeliveryEstimateLocation = {country_code: string, admin1_code?: string | undefined, city?: string | undefined, iata?: string | undefined, unlocode?: string | undefined, port_name?: string | undefined}; export type DeliveryEstimateInput = {client_reference?: string | undefined, carrier_slug: string, carrier_name?: string | undefined, transport_mode: TransportMode, completion_scope?: DeliveryCompletionScope | undefined, origin: DeliveryEstimateLocation, destination: DeliveryEstimateLocation, accepted_at?: string | undefined, shipped_at?: string | undefined, elapsed_days?: number | undefined} | {client_reference?: string | undefined, carrier_slug?: string | undefined, carrier_name: string, transport_mode: TransportMode, completion_scope?: DeliveryCompletionScope | undefined, origin: DeliveryEstimateLocation, destination: DeliveryEstimateLocation, accepted_at?: string | undefined, shipped_at?: string | undefined, elapsed_days?: number | undefined}; export type DeliveryEstimateRequest = {shipments: Array}; export type CreateApiDeliveryEstimatesArgs = {body: DeliveryEstimateRequest}; export type CreateApiDeliveryEstimatesOptions = {baseUrl: string, bearerAuth?: string | undefined, fetch?: typeof fetch | undefined}; export const transportMode = z.enum(["parcel", "postal", "express", "road_freight", "air_cargo", "ocean_container"]); export const deliveryCompletionScope = z.enum(["carrier_acceptance_to_delivery", "airport_to_airport", "airport_to_availability", "port_to_port", "port_to_availability", "port_to_gate_out"]); export const deliveryEstimateRoute = z.object({origin_dimension: z.enum(["city", "admin1", "country", "airport", "port"]).nullable().optional(), origin_key: z.string().nullable().optional(), destination_dimension: z.enum(["city", "admin1", "country", "airport", "port"]).nullable().optional(), destination_key: z.string().nullable().optional(), carrier_specific: z.boolean().optional()}); export const deliveryEstimateRange = z.object({min: z.number().gte(0).optional(), max: z.number().gte(0).optional()}); export const deliveryEstimateRemaining = z.object({mean: z.number().gte(0).nullable().optional(), median: z.number().gte(0).optional(), min: z.number().gte(0).optional(), max: z.number().gte(0).optional()}).nullable(); export const deliveryEstimateWindow = z.object({from: z.string().optional(), to: z.string().optional()}).nullable(); export const deliveryEstimateValues = z.object({mean_days: z.number().gte(0), median_days: z.number().gte(0), typical_days: deliveryEstimateRange, remaining_status: z.enum(["not_requested", "estimated", "insufficient_censored_data", "insufficient_tail_data"]), remaining_days: deliveryEstimateRemaining.optional(), estimated_delivery_at: deliveryEstimateWindow.optional()}); export const deliveryEstimateConfidence = z.object({level: z.enum(["low", "medium", "high"]), score: z.number().gte(0).lte(1), sample_count: z.number().int().gte(8), effective_sample_count: z.number().gte(0), remaining_effective_sample_count: z.number().gte(0).optional(), minimum_remaining_effective_sample_count: z.number().gte(1).optional(), survival_subject_count: z.number().int().gte(0).optional(), survival_completed_count: z.number().int().gte(0).optional(), survival_censored_count: z.number().int().gte(0).optional(), survival_risk_set: z.number().int().gte(0).optional(), survival_minimum_subject_count: z.number().int().gte(1).optional(), survival_minimum_completed_count: z.number().int().gte(1).optional(), survival_cohort_age_days: z.number().gte(0).optional(), survival_minimum_maturation_days: z.number().int().gte(1).optional(), lookback_days: z.number().int().gte(1), last_observation_at: z.string().nullable().optional(), freshness_days: z.number().gte(0).optional(), fallback_level: z.number().int().gte(0)}); export const deliveryEstimateInsufficientData = z.object({code: z.literal("INSUFFICIENT_DATA"), message: z.string(), available_sample_count: z.number().int().gte(0), minimum_sample_count: z.number().int().gte(1)}); export const deliveryEstimateResult = z.object({client_reference: z.string().optional(), carrier_slug: z.string(), transport_mode: transportMode, completion_scope: deliveryCompletionScope, route_scope_used: z.string().optional(), route: deliveryEstimateRoute.optional(), estimate: deliveryEstimateValues.optional(), confidence: deliveryEstimateConfidence.optional(), model: z.object({version: z.string().optional(), unit: z.literal("calendar_days").optional(), histogram_bucket_hours: z.number().int().optional(), remaining_method: z.enum(["kaplan_meier_right_censored_v1", "completed_tail_guard_v1"]).optional()}).optional(), error: deliveryEstimateInsufficientData.optional()}); export const deliveryEstimateResponse = z.object({results: z.array(deliveryEstimateResult), generated_at: z.string(), model_version: z.string()}); 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 }; /** * Forecast delivery time (beta) * * POST /delivery-estimates */ export const createApiDeliveryEstimates = async (args: CreateApiDeliveryEstimatesArgs, options: CreateApiDeliveryEstimatesOptions) => { const {baseUrl, fetch: fetchFn = fetch, bearerAuth} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('content-type', "application/json") headers.set('accept', "application/json") if (bearerAuth !== undefined) headers.set('authorization', `Bearer ${bearerAuth}`) const url = `${baseUrl.replace(/\/+$/, '')}/delivery-estimates${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(deliveryEstimateResponse, await res.json()) };