import {z} from 'zod' export type GetApiV1ActivitiesIdUnassignedComparisonArgs = {id: string}; export type GetApiV1ActivitiesIdUnassignedComparisonOptions = {baseUrl?: string | undefined, fetch?: typeof fetch | undefined}; export const comparisonOutcome = z.enum(["compared", "no_dropped_orders", "no_previous_run", "input_missing"]); export const runTrigger = z.enum(["scheduled", "ad_hoc", "shift_change", "not_recorded"]); export const dimensionNamesSource = z.enum(["recorded", "inferred_from_current_preferences", "unknown"]); export const runRef = z.object({activity_id: z.string(), completed_at: z.string().nullable(), solver: z.string().nullable(), result_cached: z.boolean().nullable(), trigger: runTrigger.nullable().optional(), timezone: z.string().nullable().optional(), time_anchor: z.string().nullable().optional(), time_anchor_local: z.string().nullable().optional(), units: z.record(z.string(), z.string()).nullable().optional(), capacity_dimensions: z.array(z.string()).nullable().optional(), capacity_dimensions_source: dimensionNamesSource.nullable().optional()}); export const jsonValue = z.unknown(); export const previousState = z.enum(["routed", "unassigned", "absent"]); export const unassignedCause = z.enum(["capabilities_violation_only", "fewer_vans", "vehicle_constraint_tightened", "configuration_changed", "dropped_order_changed", "demand_added", "other_orders_changed", "demand_removed", "vehicle_constraint_loosened", "more_vans", "no_input_change"]); export const dimensionLoad = z.object({position: z.number().int(), dimension: z.string().nullable(), unit: z.string().nullable(), quantity: z.number().nullable(), largest_van_capacity: z.number().nullable(), exceeds_every_van: z.boolean().nullable()}); export const localWindow = z.object({start_hours: z.number().nullable(), end_hours: z.number().nullable(), start_local: z.string().nullable(), end_local: z.string().nullable()}); export const priorityLabel = z.enum(["highest", "high", "medium", "low"]); export const overlapExample = z.object({order_id: z.string(), assignment_priority: z.number().int().nullable(), delivery_window: localWindow.nullable()}); export const overlappingRoutedOrders = z.object({count: z.number().int(), higher_priority: z.number().int(), same_priority: z.number().int(), lower_priority: z.number().int(), examples: z.array(overlapExample)}); export const droppedOrder = z.object({order_id: z.string(), leg_ids: z.array(z.string()), reasons: z.array(z.string()), detail_codes: z.array(z.number().int()), nash_diagnosis: jsonValue.nullable().optional(), previous_state: previousState.nullable(), causes: z.array(unassignedCause), primary_cause: unassignedCause.nullable(), quantities: z.array(dimensionLoad).optional(), exceeds_every_van_in: z.array(z.string()).optional(), delivery_window: localWindow.nullable().optional(), assignment_priority: z.number().int().nullable().optional(), assignment_priority_label: priorityLabel.nullable().optional(), overlapping_routed_orders: overlappingRoutedOrders.nullable().optional()}); export const orderFieldCategory = z.enum(["window", "address", "quantity", "capabilities", "service_duration", "priority", "legs", "other"]); export const fieldChange = z.object({path: z.string(), before: jsonValue.optional(), after: jsonValue.optional(), dimension: z.string().nullable().optional(), unit: z.string().nullable().optional(), before_local: z.string().nullable().optional(), after_local: z.string().nullable().optional(), before_orders: z.number().nullable().optional(), after_orders: z.number().nullable().optional()}); export const orderChange = z.object({order_id: z.string(), categories: z.array(orderFieldCategory), changes: z.array(fieldChange), assignment_priority: z.number().int().nullable().optional(), delivery_window: localWindow.nullable().optional()}); export const ordersDiff = z.object({previous_order_count: z.number().int(), current_order_count: z.number().int(), added_order_ids: z.array(z.string()), removed_order_ids: z.array(z.string()), changed: z.array(orderChange)}); export const changeDirection = z.enum(["tightened", "loosened", "not_assessed"]); export const vehicleFieldChange = z.object({path: z.string(), before: jsonValue.optional(), after: jsonValue.optional(), dimension: z.string().nullable().optional(), unit: z.string().nullable().optional(), before_local: z.string().nullable().optional(), after_local: z.string().nullable().optional(), before_orders: z.number().nullable().optional(), after_orders: z.number().nullable().optional(), direction: changeDirection}); export const vehicleTypeChange = z.object({vehicle_type_id: z.string(), changes: z.array(vehicleFieldChange)}); export const dimensionValue = z.object({position: z.number().int(), dimension: z.string().nullable(), unit: z.string().nullable(), value: z.number().nullable()}); export const vanFacts = z.object({vehicle_type_id: z.string(), count: z.number().int(), capacity: z.array(dimensionValue), max_orders_per_route: z.number().nullable(), orders_per_route: z.number().nullable().optional(), shift_start_local: z.string().nullable(), shift_end_local: z.string().nullable()}); export const vehiclesDiff = z.object({previous_vans: z.number().int(), current_vans: z.number().int(), previous_order_slots: z.number().int().nullable(), current_order_slots: z.number().int().nullable(), previous_retained_order_slots: z.number().int().nullable(), current_retained_order_slots: z.number().int().nullable(), order_slots_unit: z.string().nullable().optional(), previous_order_slots_orders: z.number().nullable().optional(), current_order_slots_orders: z.number().nullable().optional(), added_type_ids: z.array(z.string()), removed_type_ids: z.array(z.string()), changed: z.array(vehicleTypeChange), current_fleet: z.array(vanFacts).optional()}); export const breakChange = z.object({break_id: z.string(), kind: z.enum(["added", "removed", "changed"]), owner_vehicle_type_ids: z.array(z.string()), owned_by_retained_type: z.boolean(), changes: z.array(fieldChange)}); export const keyedChange = z.object({id: z.string(), kind: z.enum(["added", "removed", "changed"]), changes: z.array(fieldChange)}); export const configurationDiff = z.object({solver_parameters: z.array(fieldChange), units: z.array(fieldChange), start_depots: z.array(keyedChange), end_depots: z.array(keyedChange), route_restrictions: z.array(keyedChange), user_tags_added: z.array(z.string()), user_tags_removed: z.array(z.string()), time_anchor_changed: z.boolean(), other_keys: z.array(fieldChange), previous_solver: z.string().nullable(), current_solver: z.string().nullable()}); export const causeMatch = z.object({cause: unassignedCause, order_ids: z.array(z.string()), summary: z.string()}); export const fleetPoint = z.object({activity_id: z.string(), completed_at: z.string(), vans: z.number().int(), order_slots: z.number().int().nullable()}); export const nashSetting = z.enum(["van_departure_delay", "coles_per_route_order_cap", "shift_change_search_level"]); export const settingOwner = z.enum(["shift_roster", "nash"]); export const derivedSource = z.object({setting: nashSetting, owner: settingOwner, description: z.string(), before: z.number().nullable(), after: z.number().nullable(), unit: z.string().nullable(), vehicle_type_ids: z.array(z.string()), explains: z.array(z.string()), evidence: z.string()}); export const unassignedComparison = z.object({schema_version: z.literal("unassigned-comparison/v1").optional(), outcome: comparisonOutcome, current_run: runRef, previous_run: runRef.nullable(), dropped_orders: z.array(droppedOrder), newly_dropped_order_ids: z.array(z.string()), carried_over_order_ids: z.array(z.string()), orders: ordersDiff.nullable(), vehicles: vehiclesDiff.nullable(), breaks: z.array(breakChange), configuration: configurationDiff.nullable(), loosening_changes: z.array(fieldChange), causes: z.array(causeMatch), primary_cause: unassignedCause.nullable(), recent_fleet: z.array(fleetPoint), blind_spots: z.array(z.string()), dimension_units: z.record(z.string(), z.string()).optional(), derived_sources: z.array(derivedSource).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 }; /** * Compare an optimization run with the previous run * * GET /v1/activities/{id}/unassigned-comparison */ export const getApiV1ActivitiesIdUnassignedComparison = async (args: GetApiV1ActivitiesIdUnassignedComparisonArgs, options?: GetApiV1ActivitiesIdUnassignedComparisonOptions) => { const {baseUrl = "https://api.sandbox.usenash.com", fetch: fetchFn = fetch} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('accept', "application/json") const url = `${baseUrl.replace(/\/+$/, '')}/v1/activities/${encodeURIComponent(String(args.id))}/unassigned-comparison${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(unassignedComparison, await res.json()) };