@solvice/routing 0.1.0-beta.0 → 0.1.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,33 +16,51 @@ import { createSolvice, SolviceError } from '@solvice/routing';
16
16
  const solvice = createSolvice({ apiKey: process.env.SOLVICE_API_KEY! });
17
17
 
18
18
  try {
19
- const solution = await solvice.solve(
20
- {
21
- problem: {
22
- jobs: [{ id: 'delivery-1', location: [4.7005, 50.8798], service_duration_s: 300 }],
23
- vehicles: [
24
- {
25
- id: 'truck-1',
26
- shifts: [
27
- {
28
- id: 'mon',
29
- from: '2026-04-01T08:00:00+02:00',
30
- to: '2026-04-01T17:00:00+02:00',
31
- start: { location: [4.3517, 50.8503] },
32
- end: { location: [4.3517, 50.8503] },
33
- },
34
- ],
35
- },
36
- ],
37
- },
38
- options: { time_limit_s: 5 },
19
+ const solution = await solvice.solve({
20
+ problem: {
21
+ jobs: [
22
+ {
23
+ id: 'delivery-1',
24
+ location: [4.7005, 50.8798], // [longitude, latitude]
25
+ service_duration_s: 300,
26
+ time_windows: [{ from: '2026-04-01T09:00:00+02:00', to: '2026-04-01T12:00:00+02:00' }],
27
+ delivery: { weight: 10 },
28
+ },
29
+ ],
30
+ vehicles: [
31
+ {
32
+ id: 'truck-1',
33
+ capacity: { weight: 100 },
34
+ shifts: [
35
+ {
36
+ id: 'mon',
37
+ from: '2026-04-01T08:00:00+02:00',
38
+ to: '2026-04-01T17:00:00+02:00',
39
+ start: { location: [4.3517, 50.8503] },
40
+ end: { location: [4.3517, 50.8503] },
41
+ },
42
+ ],
43
+ },
44
+ ],
39
45
  },
40
- { signal: AbortSignal.timeout(35_000) },
41
- );
42
- console.log(solution.routes);
46
+ options: { time_limit_s: 5 },
47
+ });
48
+
49
+ for (const route of solution.routes) {
50
+ console.log(`${route.vehicle} / ${route.shift}: ${route.distance_m} m`);
51
+ for (const stop of route.stops) {
52
+ console.log(` ${stop.type} ${stop.id ?? ''} arrives ${stop.arrival}`);
53
+ }
54
+ }
55
+ for (const job of solution.unassigned) {
56
+ console.log(`unassigned ${job.job_id}: ${job.category}`);
57
+ }
43
58
  } catch (err) {
44
59
  if (!(err instanceof SolviceError)) throw err;
45
- console.error(err.status, err.body?.errors);
60
+ console.error(err.status, err.body?.code, err.message);
61
+ for (const item of err.body?.errors ?? []) {
62
+ console.error(` ${item.code} at ${item.pointer}: ${item.message}`);
63
+ }
46
64
  }
47
65
  ```
48
66
 
@@ -53,8 +71,12 @@ try {
53
71
  | `suggest(request)` | `POST /v3/routing/suggest` |
54
72
  | `validate.solve(request)`, `validate.evaluate(request)`, `validate.suggest(request)` | the same three with `?validate_only=true` |
55
73
 
56
- Any non-2xx response throws a `SolviceError` with the HTTP `status` and, when the
57
- response carried one, the API's error `body`.
74
+ Any non-2xx response throws a `SolviceError` with the HTTP `status`, the
75
+ `response`, and, when the response carried one, the API's error `body`. A `401`
76
+ or a `429` comes from the API gateway and does not use that body (see
77
+ [Errors](https://docs.solvice.io/guides/vrp/errors)): branch on `status`, and read
78
+ `raw` for whatever JSON came back. Every `503` carries `Retry-After`; wait
79
+ `err.retryAfterS` seconds before retrying.
58
80
 
59
81
  Every method takes a second argument, `{ signal, headers }`. The client sets no
60
82
  timeout of its own: pass `AbortSignal.timeout(ms)` as `signal`, with a margin on
@@ -76,6 +98,11 @@ npm test # builds, typechecks tests/types.ts and examples/, runs the u
76
98
  npm run smoke # live calls; needs SOLVICE_API_KEY
77
99
  ```
78
100
 
79
- To release, bump `version` to the next `0.x.y-beta.n` and run `npm run publish:beta`.
80
- The explicit `--tag beta` matters: npm 11 ignores `publishConfig.tag` and would
81
- move `latest` to the prerelease.
101
+ To release, bump `version` to the next `0.x.y-beta.n`, merge it to `main`, then run
102
+ the "Publish TypeScript SDK" workflow on `main` (Actions → Run workflow). It runs
103
+ `npm run publish:beta` through npm trusted publishing; never publish
104
+ from a branch. The explicit `--tag beta` matters: npm 11 ignores
105
+ `publishConfig.tag` and would move `latest` to the prerelease. Until a stable version
106
+ exists, `latest` still points at the first prerelease (npm gives a package's first
107
+ version `latest` whatever the tag), so `npm install @solvice/routing` without
108
+ `@beta` installs the beta too.
package/dist/index.d.ts CHANGED
@@ -19,12 +19,28 @@ export interface RequestOptions {
19
19
  signal?: AbortSignal;
20
20
  headers?: Record<string, string>;
21
21
  }
22
- /** Any non-2xx response. `body` is absent when the response carried no JSON (401, 429). */
22
+ /**
23
+ * Any non-2xx response. `body` is the API's error body when the response
24
+ * carried one. A `401` or a `429` comes from the API gateway and does not use
25
+ * that body (a JSON `{ message, error }` or nothing): branch on `status`, and
26
+ * read `raw` for whatever came back. `response` is the `Response` itself, for
27
+ * its headers: every `503` carries `Retry-After` (`retryAfterS`), and
28
+ * `X-Request-Id` survives a bodiless error.
29
+ */
23
30
  export declare class SolviceError extends Error {
24
31
  readonly status: number;
32
+ readonly response: Response;
25
33
  readonly body?: ErrorBody | undefined;
26
- constructor(status: number, body?: ErrorBody | undefined);
34
+ /** The parsed response body as received, whatever its shape; `undefined` when there was none. */
35
+ readonly raw?: unknown | undefined;
36
+ constructor(status: number, response: Response, body?: ErrorBody | undefined,
37
+ /** The parsed response body as received, whatever its shape; `undefined` when there was none. */
38
+ raw?: unknown | undefined);
39
+ /** Seconds to wait before retrying, from the `Retry-After` header; `undefined` without one (or with an HTTP date). */
40
+ get retryAfterS(): number | undefined;
27
41
  }
42
+ /** The client `createSolvice` returns, for naming it in your own signatures. */
43
+ export type Solvice = ReturnType<typeof createSolvice>;
28
44
  export declare function createSolvice({ apiKey, baseUrl, fetch }: SolviceOptions): {
29
45
  solve: (body: SolveRequest, options?: RequestOptions) => Promise<SolveResponse>;
30
46
  evaluate: (body: EvaluateRequest, options?: RequestOptions) => Promise<SolveResponse>;
package/dist/index.js CHANGED
@@ -1,19 +1,45 @@
1
1
  import createClient from "openapi-fetch";
2
- /** Any non-2xx response. `body` is absent when the response carried no JSON (401, 429). */
2
+ /**
3
+ * Any non-2xx response. `body` is the API's error body when the response
4
+ * carried one. A `401` or a `429` comes from the API gateway and does not use
5
+ * that body (a JSON `{ message, error }` or nothing): branch on `status`, and
6
+ * read `raw` for whatever came back. `response` is the `Response` itself, for
7
+ * its headers: every `503` carries `Retry-After` (`retryAfterS`), and
8
+ * `X-Request-Id` survives a bodiless error.
9
+ */
3
10
  export class SolviceError extends Error {
4
11
  status;
12
+ response;
5
13
  body;
6
- constructor(status, body) {
7
- super(body?.message ?? `Solvice API responded ${status}`);
14
+ raw;
15
+ constructor(status, response, body,
16
+ /** The parsed response body as received, whatever its shape; `undefined` when there was none. */
17
+ raw) {
18
+ super(body?.message ?? gatewayMessage(raw) ?? `Solvice API responded ${status}`);
8
19
  this.status = status;
20
+ this.response = response;
9
21
  this.body = body;
22
+ this.raw = raw;
10
23
  this.name = "SolviceError";
11
24
  }
25
+ /** Seconds to wait before retrying, from the `Retry-After` header; `undefined` without one (or with an HTTP date). */
26
+ get retryAfterS() {
27
+ const value = this.response.headers.get("retry-after");
28
+ if (value === null)
29
+ return undefined;
30
+ const seconds = Number(value);
31
+ return Number.isFinite(seconds) ? seconds : undefined;
32
+ }
12
33
  }
34
+ const isRecord = (value) => typeof value === "object" && value !== null;
35
+ /** The API's error body has `status`, `code` and `message`; the gateway's `401` has `message` and `error`. */
36
+ const isErrorBody = (value) => isRecord(value) && "status" in value && "code" in value && "message" in value;
37
+ const gatewayMessage = (raw) => isRecord(raw) && typeof raw.message === "string" ? raw.message : undefined;
13
38
  function unwrap({ data, error, response }) {
14
39
  if (!response.ok) {
15
- // openapi-fetch hands back the raw text when the error is not JSON.
16
- throw new SolviceError(response.status, typeof error === "object" && error !== null ? error : undefined);
40
+ // openapi-fetch hands back the raw text when the error is not JSON, and "" for no body.
41
+ const raw = error === "" || error == null ? undefined : error;
42
+ throw new SolviceError(response.status, response, isErrorBody(raw) ? raw : undefined, raw);
17
43
  }
18
44
  return data;
19
45
  }
package/dist/v3.d.ts CHANGED
@@ -34,7 +34,7 @@ export interface paths {
34
34
  put?: never;
35
35
  /**
36
36
  * Solve a vehicle routing problem
37
- * @description Assigns the jobs and shipments in `problem` to the vehicles' shifts and returns the routes, the `plan` they follow and any tasks left unassigned, each with the reasons. The request is validated first: an invalid one is rejected with a 400 whose `errors[]` says what to fix. Travel times and distances for the request's locations are then looked up, and the search runs for `options.time_limit_s` seconds (default 1). Send a previous result's `plan` to re-optimize starting from it. `POST /v3/routing/evaluate` and `POST /v3/routing/suggest` take the same `problem`.
37
+ * @description Assigns the jobs and shipments in `problem` to the vehicles' shifts and returns the routes, the `plan` they follow and any tasks left unassigned, each with the reasons. The request is validated first: an invalid one is rejected with a 400 whose `errors[]` says what to fix. Travel times and distances for the request's locations are then looked up, and the search runs for `options.time_limit_s` seconds (default 150 ms plus 4 ms per task; maximum 60). Send a previous result's `plan` to re-optimize starting from it. `POST /v3/routing/evaluate` and `POST /v3/routing/suggest` take the same `problem`.
38
38
  */
39
39
  post: operations["solve"];
40
40
  delete?: never;
@@ -84,13 +84,14 @@ export interface components {
84
84
  cost: number;
85
85
  /**
86
86
  * @description Vehicle id; applies to every shift of that vehicle. An id that matches
87
- * no vehicle is rejected with HTTP 400.
87
+ * no vehicle, or a vehicle listed twice in one task's
88
+ * `assignment_costs`, is rejected with HTTP 400.
88
89
  * @example v1
89
90
  */
90
91
  vehicle: string;
91
92
  };
92
93
  /**
93
- * @description Named capacity/demand/load dimensions, e.g. `{"weight": 30, "volume": 2}`. Keys are user-chosen dimension names; values are integer quantities. Order-independent and self-documenting.
94
+ * @description Named capacity/demand/load dimensions, e.g. `{"weight": 30, "volume": 2}`. Keys are user-chosen dimension names; values are integer quantities. Order-independent and self-documenting. In a request every value must be `>= 0` (a negative one is rejected with HTTP 400 `out_of_range`), and a request may use at most 4 distinct dimension names across jobs, shipments and vehicles (HTTP 400 `limit_exceeded` beyond that).
94
95
  * @example {
95
96
  * "volume": 2,
96
97
  * "weight": 20
@@ -127,10 +128,15 @@ export interface components {
127
128
  Break: {
128
129
  /**
129
130
  * Format: int32
130
- * @description Break duration in seconds.
131
+ * @description Break duration in seconds. Must be `> 0`.
131
132
  */
132
133
  duration_s: number;
133
- /** @description Where the break is taken. Absent: wherever the vehicle is. */
134
+ /**
135
+ * @description Accepted and range-checked like any place, but it has no effect:
136
+ * the break is scheduled wherever the vehicle is when it starts, and
137
+ * the response's `break` stop carries no `location`. The coordinate
138
+ * is still sent to the travel-time lookup.
139
+ */
134
140
  location?: null | components["schemas"]["Place"];
135
141
  /** @enum {string} */
136
142
  type: "windowed";
@@ -144,7 +150,7 @@ export interface components {
144
150
  after: components["schemas"]["TriggerAfter"];
145
151
  /**
146
152
  * Format: int32
147
- * @description Break duration in seconds.
153
+ * @description Break duration in seconds. Must be `> 0`.
148
154
  */
149
155
  duration_s: number;
150
156
  /** @description Not supported yet: any value is rejected with HTTP 400. */
@@ -160,6 +166,7 @@ export interface components {
160
166
  * Format: int64
161
167
  * @description Trigger threshold in seconds (strict `>` — the break fires when the
162
168
  * accumulator exceeds this). EU 561/2006 drive rule: `16200` (4 h 30).
169
+ * Must be `> 0` and fit in 32 bits.
163
170
  * @example 14400
164
171
  */
165
172
  threshold_s: number;
@@ -275,7 +282,8 @@ export interface components {
275
282
  /**
276
283
  * Format: double
277
284
  * @description Money charged once per reload visit, in the objective's currency.
278
- * Default 0. A shift that only starts or ends here is not charged.
285
+ * Default 0; at most `1e9`. A shift that only starts or ends here is not
286
+ * charged.
279
287
  *
280
288
  * Set this when a reload has a real per-visit price the travel terms
281
289
  * cannot see — typically the driver labour for the unload itself
@@ -316,9 +324,15 @@ export interface components {
316
324
  * }
317
325
  */
318
326
  Eligibility: {
319
- /** @description Hard whitelist of vehicle ids. Omit = all vehicles allowed. */
327
+ /**
328
+ * @description Hard whitelist of vehicle ids. Omit = all vehicles allowed. An empty
329
+ * list, or an id that names no vehicle, is rejected with HTTP 400.
330
+ */
320
331
  allowed?: string[] | null;
321
- /** @description Hard blacklist of vehicle ids. Mutually exclusive with `allowed`. */
332
+ /**
333
+ * @description Hard blacklist of vehicle ids. Mutually exclusive with `allowed`. An
334
+ * id that names no vehicle is rejected with HTTP 400.
335
+ */
322
336
  excluded?: string[] | null;
323
337
  };
324
338
  /**
@@ -497,7 +511,7 @@ export interface components {
497
511
  distance?: null | components["schemas"]["GroupingDistance"];
498
512
  /**
499
513
  * Format: int32
500
- * @description Radius defining a group, in metres. Default `180`.
514
+ * @description Radius defining a group, in metres. Default `180`. Must be `> 0`.
501
515
  * @example 180
502
516
  */
503
517
  radius_m?: number | null;
@@ -646,8 +660,10 @@ export interface components {
646
660
  */
647
661
  insertions: components["schemas"]["InsertionOption"][];
648
662
  /**
649
- * @description The unit's lead: its first member in placement order (the visit, the
650
- * shipment, or the first relation member).
663
+ * @description The unit's lead: its first member in placement order. That is the
664
+ * visit, the shipment, the first stop of a `sequence` chain in the
665
+ * unit, or else the member that comes first in problem order (`jobs`,
666
+ * then `shipments`).
651
667
  * @example job-2
652
668
  */
653
669
  job_id: string;
@@ -736,6 +752,8 @@ export interface components {
736
752
  * @example 60
737
753
  */
738
754
  per_late_hour?: number | null;
755
+ /** @description Spreads work across vehicles. See the `WorkloadShare` schema. */
756
+ workload_share?: null | components["schemas"]["WorkloadShare"];
739
757
  };
740
758
  /**
741
759
  * @description The 200 body of solve, evaluate and suggest: the call's result, or with
@@ -808,14 +826,15 @@ export interface components {
808
826
  from?: string | null;
809
827
  /**
810
828
  * Format: int64
811
- * @description Hard cap on the summed distance driven, in metres.
829
+ * @description Hard cap on the summed distance driven, in metres. Must be `>= 0`.
812
830
  * @example 900000
813
831
  */
814
832
  max_distance_m?: number | null;
815
833
  /**
816
834
  * Format: int64
817
835
  * @description Hard cap on the summed route span of the period's shifts, measured as
818
- * for `max_route_duration_s`. An unused shift adds nothing.
836
+ * for `max_route_duration_s`, in seconds. An unused shift adds nothing.
837
+ * Must be `>= 0`.
819
838
  * @example 144000
820
839
  */
821
840
  max_duration_s?: number | null;
@@ -834,7 +853,7 @@ export interface components {
834
853
  to?: string | null;
835
854
  };
836
855
  /**
837
- * @description A place: a coordinate `[longitude, latitude]` in GeoJSON order — exactly two numbers. Tasks at the same coordinate are treated as one location.
856
+ * @description A place: a coordinate `[longitude, latitude]` in GeoJSON order — exactly two numbers. Tasks at the same coordinate are treated as one location. Longitude must lie in `[-180, 180]` and latitude in `[-90, 90]` (HTTP 400 `out_of_range` otherwise), and `[0, 0]` is rejected with HTTP 400 `invalid_value` as a missing-GPS placeholder.
838
857
  * @example [
839
858
  * 3.725,
840
859
  * 51.05
@@ -996,7 +1015,8 @@ export interface components {
996
1015
  */
997
1016
  Preference: {
998
1017
  /**
999
- * @description Preferred vehicle id.
1018
+ * @description Preferred vehicle id. An id that names no vehicle, or a vehicle listed
1019
+ * twice in one task's `preferences`, is rejected with HTTP 400.
1000
1020
  * @example v1
1001
1021
  */
1002
1022
  vehicle: string;
@@ -1059,19 +1079,25 @@ export interface components {
1059
1079
  ProblemInput: {
1060
1080
  /** @description Depots that shifts start from, end at, or reload at, by id. */
1061
1081
  depots?: components["schemas"]["Depot"][];
1062
- /** @description Single-stop visits to serve. */
1082
+ /**
1083
+ * @description Single-stop visits to serve. At most 10,000 tasks per request, `jobs`
1084
+ * and `shipments` combined (HTTP 400 `limit_exceeded` beyond that).
1085
+ */
1063
1086
  jobs?: components["schemas"]["Visit"][];
1064
1087
  /** @description Objective configuration. */
1065
1088
  objective?: null | components["schemas"]["Objective"];
1066
- /** @description Stop caps shared by a set of vehicles. */
1089
+ /** @description Stop caps shared by a set of vehicles. At most 65,535. */
1067
1090
  quotas?: components["schemas"]["Quota"][];
1068
1091
  /** @description Ordering, grouping, and synchronization constraints. */
1069
1092
  relations?: components["schemas"]["Relation"][] | null;
1070
- /** @description Bound pickup→delivery shipments to serve. */
1093
+ /**
1094
+ * @description Bound pickup→delivery shipments to serve. Counts toward the same
1095
+ * 10,000-task cap as `jobs`.
1096
+ */
1071
1097
  shipments?: components["schemas"]["Shipment"][];
1072
1098
  /** @description Where travel times and distances come from. */
1073
1099
  travel?: null | components["schemas"]["Travel"];
1074
- /** @description Available vehicles. */
1100
+ /** @description Available vehicles: 1 to 2,000 (HTTP 400 otherwise). */
1075
1101
  vehicles: components["schemas"]["Vehicle"][];
1076
1102
  };
1077
1103
  /**
@@ -1105,7 +1131,9 @@ export interface components {
1105
1131
  Quota: {
1106
1132
  /**
1107
1133
  * Format: date-time
1108
- * @description Window start (RFC 3339, UTC offset required), with `to`.
1134
+ * @description Window start (RFC 3339, UTC offset required), with `to`. The window
1135
+ * must overlap a shift of one of `vehicles` (counting its
1136
+ * `max_overtime_s`), else the request is rejected with HTTP 400.
1109
1137
  * @example 2026-10-05T00:00:00+02:00
1110
1138
  */
1111
1139
  from?: string | null;
@@ -1114,7 +1142,10 @@ export interface components {
1114
1142
  * `/evaluate` `quota` violation.
1115
1143
  */
1116
1144
  id: string;
1117
- /** @description Job or shipment ids whose stops count, each at most once. */
1145
+ /**
1146
+ * @description Job or shipment ids whose stops count, each at most once. At least
1147
+ * one; an id that names no job or shipment is rejected with HTTP 400.
1148
+ */
1118
1149
  job_ids: string[];
1119
1150
  /**
1120
1151
  * Format: int32
@@ -1127,7 +1158,10 @@ export interface components {
1127
1158
  * @example 2026-10-06T00:00:00+02:00
1128
1159
  */
1129
1160
  to?: string | null;
1130
- /** @description Vehicle ids that share the cap, each at most once. */
1161
+ /**
1162
+ * @description Vehicle ids that share the cap, each at most once. At least one; an id
1163
+ * that names no vehicle is rejected with HTTP 400.
1164
+ */
1131
1165
  vehicles: string[];
1132
1166
  };
1133
1167
  /**
@@ -1137,15 +1171,22 @@ export interface components {
1137
1171
  * Each type carries only its own fields; any other `type` is rejected with
1138
1172
  * HTTP 400.
1139
1173
  *
1140
- * Members are named by `stops` (stop ids). `same_vehicle`, `same_day` and
1174
+ * Members are named by `stops` (job ids, or shipment-leg stop ids where
1175
+ * the type accepts them, see below). `same_vehicle`, `same_day` and
1141
1176
  * `same_route` take **either** `stops` **or** a `tag`, not both. Every
1142
1177
  * relation is hard.
1143
1178
  *
1144
- * Relations constrain jobs, not shipments. A shipment id in the `stops` of
1145
- * a `sequence`, `same_vehicle`, `same_day` or `synchronized` relation, and
1146
- * a shipment carrying the `tag` of a `same_vehicle` or `same_day` relation,
1147
- * passes validation but is skipped: the request returns HTTP 200 and the
1148
- * relation does not constrain that shipment. `same_route`, `time_lag`,
1179
+ * A shipment takes part through its legs: `sequence`, `same_vehicle`,
1180
+ * `same_day` and `same_route` accept a leg's stop id (`pickup.id` /
1181
+ * `delivery.id`, by default `"{shipment}:pickup"` / `":delivery"`) and bind
1182
+ * that leg like a job. A leg coupled to other tasks by a `synchronized` or
1183
+ * `time_lag` relation (directly, or through a `sequence` with a member that
1184
+ * is) is rejected with HTTP 400, as is a `sequence` putting a delivery before
1185
+ * its pickup. A shipment's own id in the `stops` of a `sequence`,
1186
+ * `same_vehicle`, `same_day` or `synchronized` relation, and a shipment
1187
+ * carrying the `tag` of a `same_vehicle` or `same_day` relation, passes
1188
+ * validation but is skipped: the request returns HTTP 200 and the relation
1189
+ * does not constrain that shipment. `same_route`, `time_lag`,
1149
1190
  * `compatible_vehicles`, `all_or_none` and `tag_order` reject a shipment
1150
1191
  * with HTTP 400.
1151
1192
  * @example {
@@ -1158,9 +1199,9 @@ export interface components {
1158
1199
  */
1159
1200
  Relation: {
1160
1201
  /**
1161
- * @description Job or shipment ids, in the order they must be served. A shipment
1162
- * is named by its id, not by a leg's stop id (stop-id members are
1163
- * not resolved yet).
1202
+ * @description Job ids or shipment-leg stop ids (`pickup.id` / `delivery.id`, by
1203
+ * default `"{shipment}:pickup"` / `"{shipment}:delivery"`), in the
1204
+ * order they must be served. A shipment's own id is not resolved.
1164
1205
  */
1165
1206
  stops: string[];
1166
1207
  /** @enum {string} */
@@ -1172,8 +1213,8 @@ export interface components {
1172
1213
  type: "tag_order";
1173
1214
  } | {
1174
1215
  /**
1175
- * @description Job or shipment ids in the relation (mutually exclusive with
1176
- * `tag`; stop-id members are not resolved yet).
1216
+ * @description Job ids or shipment-leg stop ids in the relation (mutually
1217
+ * exclusive with `tag`). A shipment's own id is not resolved.
1177
1218
  */
1178
1219
  stops?: string[] | null;
1179
1220
  /** @description Tag selecting the relation members (mutually exclusive with `stops`). */
@@ -1182,8 +1223,8 @@ export interface components {
1182
1223
  type: "same_vehicle";
1183
1224
  } | {
1184
1225
  /**
1185
- * @description Job or shipment ids in the relation (mutually exclusive with
1186
- * `tag`; stop-id members are not resolved yet).
1226
+ * @description Job ids or shipment-leg stop ids in the relation (mutually
1227
+ * exclusive with `tag`). A shipment's own id is not resolved.
1187
1228
  */
1188
1229
  stops?: string[] | null;
1189
1230
  /** @description Tag selecting the relation members (mutually exclusive with `stops`). */
@@ -1192,8 +1233,9 @@ export interface components {
1192
1233
  type: "same_day";
1193
1234
  } | {
1194
1235
  /**
1195
- * @description Job or shipment ids in the relation (mutually exclusive with
1196
- * `tag`; stop-id members are not resolved yet).
1236
+ * @description Job ids or shipment-leg stop ids in the relation (mutually
1237
+ * exclusive with `tag`). A shipment's own id is rejected with HTTP
1238
+ * 400.
1197
1239
  */
1198
1240
  stops?: string[] | null;
1199
1241
  /** @description Tag selecting the relation members (mutually exclusive with `stops`). */
@@ -1204,8 +1246,8 @@ export interface components {
1204
1246
  /**
1205
1247
  * Format: int64
1206
1248
  * @description Maximum spread between the synchronized instants in seconds
1207
- * (`0` = exact simultaneity). Required. A negative value is rejected
1208
- * with HTTP 400 `out_of_range`.
1249
+ * (`0` = exact simultaneity). Required. A negative value, or one that
1250
+ * does not fit in 32 bits, is rejected with HTTP 400 `out_of_range`.
1209
1251
  */
1210
1252
  max_wait_s: number;
1211
1253
  /**
@@ -1214,8 +1256,9 @@ export interface components {
1214
1256
  */
1215
1257
  on?: null | components["schemas"]["StopInstant"];
1216
1258
  /**
1217
- * @description Participating job or shipment ids (stop-id members are not
1218
- * resolved yet).
1259
+ * @description Participating job ids, at most 8 (HTTP 400 `limit_exceeded`
1260
+ * beyond that). A shipment-leg stop id is rejected with HTTP 400;
1261
+ * a shipment's own id is not resolved.
1219
1262
  */
1220
1263
  stops: string[];
1221
1264
  /** @enum {string} */
@@ -1226,11 +1269,13 @@ export interface components {
1226
1269
  /**
1227
1270
  * Format: int64
1228
1271
  * @description Upper bound on `t(to) − t(from)` in seconds. Absent = unbounded.
1272
+ * Must fit in 32 bits and be `>= min_s` (HTTP 400 otherwise).
1229
1273
  */
1230
1274
  max_s?: number | null;
1231
1275
  /**
1232
1276
  * Format: int64
1233
- * @description Lower bound on `t(to) − t(from)` in seconds. Default `0`.
1277
+ * @description Lower bound on `t(to) − t(from)` in seconds. Default `0`. Must fit
1278
+ * in 32 bits.
1234
1279
  */
1235
1280
  min_s?: number | null;
1236
1281
  /** @description The bounded end. */
@@ -1239,8 +1284,8 @@ export interface components {
1239
1284
  type: "time_lag";
1240
1285
  } | {
1241
1286
  /**
1242
- * @description Job or shipment ids, at least two (stop-id members are not
1243
- * resolved yet).
1287
+ * @description Job ids, 2 to 16. A shipment's own id or a shipment-leg stop id
1288
+ * is rejected with HTTP 400.
1244
1289
  * @example [
1245
1290
  * "job-1",
1246
1291
  * "job-2"
@@ -1251,7 +1296,8 @@ export interface components {
1251
1296
  type: "compatible_vehicles";
1252
1297
  } | {
1253
1298
  /**
1254
- * @description Job ids, at least two.
1299
+ * @description Job ids, at least two. A shipment's own id or a shipment-leg stop id
1300
+ * is rejected with HTTP 400.
1255
1301
  * @example [
1256
1302
  * "job-1",
1257
1303
  * "job-2"
@@ -1462,7 +1508,8 @@ export interface components {
1462
1508
  * "target": 0,
1463
1509
  * "time": 57.1536,
1464
1510
  * "waiting": 0,
1465
- * "window_choice": 0
1511
+ * "window_choice": 0,
1512
+ * "workload_share": 0
1466
1513
  * },
1467
1514
  * "cost": 58.8436,
1468
1515
  * "currency": "EUR",
@@ -1556,8 +1603,8 @@ export interface components {
1556
1603
  /**
1557
1604
  * Format: double
1558
1605
  * @description Everything else the solver charges, in currency units: vehicle
1559
- * preferences (`preferences[].violation_cost`) and the
1560
- * non-preferred-date cost.
1606
+ * preferences (`preferences[].violation_cost`), `assignment_costs`, and
1607
+ * the non-preferred-date cost (`objective.non_preferred_date_cost`).
1561
1608
  */
1562
1609
  other: number;
1563
1610
  /**
@@ -1596,6 +1643,12 @@ export interface components {
1596
1643
  * units (`time_windows[].cost`).
1597
1644
  */
1598
1645
  window_choice: number;
1646
+ /**
1647
+ * Format: double
1648
+ * @description Workload outside the equal-share band, in currency units
1649
+ * (`objective.workload_share`).
1650
+ */
1651
+ workload_share: number;
1599
1652
  };
1600
1653
  /**
1601
1654
  * @description Per-vehicle work-rate override for one task's `service_duration_s`.
@@ -1708,7 +1761,7 @@ export interface components {
1708
1761
  * Format: int32
1709
1762
  * @description Maximum allowed overtime beyond `to` in seconds. Overtime is reached
1710
1763
  * by working late, never by waiting: a `synchronized` or `time_lag`
1711
- * hold stops at `to`.
1764
+ * hold stops at `to`. Default: none (`to` is hard). Must be `>= 0`.
1712
1765
  * @example 1800
1713
1766
  */
1714
1767
  max_overtime_s?: number | null;
@@ -1719,10 +1772,11 @@ export interface components {
1719
1772
  * means a single trip — reloads are permitted in `reloads` but the cap
1720
1773
  * prevents ever using one. `reloads` present with `max_trips` absent
1721
1774
  * means unlimited trips. A single reload depot can only be
1722
- * revisited up to 8 times per vehicle regardless of `max_trips`;
1775
+ * revisited up to 8 times per shift regardless of `max_trips`;
1723
1776
  * a `max_trips` beyond that against one depot
1724
1777
  * is accepted but the extra headroom is unreachable — list more than
1725
- * one reload depot to raise the practical ceiling.
1778
+ * one reload depot to raise the practical ceiling. `0` is rejected with
1779
+ * HTTP 400 (the first trip already counts as trip 1).
1726
1780
  * @example 2
1727
1781
  */
1728
1782
  max_trips?: number | null;
@@ -1816,13 +1870,15 @@ export interface components {
1816
1870
  ShiftLimits: {
1817
1871
  /**
1818
1872
  * Format: int64
1819
- * @description Hard cap on this shift's route distance, in metres.
1873
+ * @description Hard cap on this shift's route distance, in metres. `0` to
1874
+ * `2147483647`, as on the vehicle.
1820
1875
  * @example 200000
1821
1876
  */
1822
1877
  max_distance_m?: number | null;
1823
1878
  /**
1824
1879
  * Format: int64
1825
- * @description Hard cap on this shift's route span, in seconds.
1880
+ * @description Hard cap on this shift's route span, in seconds. `0` to
1881
+ * `2147483647`, as on the vehicle.
1826
1882
  * @example 32400
1827
1883
  */
1828
1884
  max_route_duration_s?: number | null;
@@ -1904,10 +1960,17 @@ export interface components {
1904
1960
  * `score.components.drop_fees` and summed into `score.cost`.
1905
1961
  *
1906
1962
  * On a shipment the fee applies to the pair, charged once.
1963
+ *
1964
+ * Must be finite, `>= 0` and at most `1e9`; combined with `priority` it
1965
+ * is rejected with HTTP 400 (`conflicting_fields`).
1907
1966
  * @example 25
1908
1967
  */
1909
1968
  drop_fee?: number | null;
1910
- /** @description Hard vehicle allow/exclude. */
1969
+ /**
1970
+ * @description Hard vehicle allow/exclude. A request may use at most 256 distinct
1971
+ * permitted-vehicle sets (after resolving `allowed`/`excluded`) across
1972
+ * all jobs and shipments (HTTP 400 `limit_exceeded` beyond that).
1973
+ */
1911
1974
  eligible_vehicles?: null | components["schemas"]["Eligibility"];
1912
1975
  /**
1913
1976
  * @description Shipment id: 1 to 128 characters, unique across `jobs` + `shipments`.
@@ -1984,7 +2047,9 @@ export interface components {
1984
2047
  service_factors?: components["schemas"]["ServiceFactor"][] | null;
1985
2048
  /**
1986
2049
  * @description Hard skill requirements — the assigned vehicle must provide every one,
1987
- * at or above each requirement's `min_level`.
2050
+ * at or above each requirement's `min_level`. A request may use at most
2051
+ * 256 distinct `(name, min_level)` pairs across all jobs and shipments
2052
+ * (HTTP 400 `limit_exceeded` beyond that).
1988
2053
  */
1989
2054
  skills?: components["schemas"]["SkillReq"][] | null;
1990
2055
  /**
@@ -1993,6 +2058,9 @@ export interface components {
1993
2058
  * `serves_tags` (hard ANY-overlap) or a vehicle prices them via
1994
2059
  * `preferred_tags` (soft), for tag matching. ANY-overlap — contrast with
1995
2060
  * `skills`, which requires ALL. Not capability matching on its own.
2061
+ * A request may carry at most 256 distinct tags across all jobs and
2062
+ * shipments (HTTP 400 `limit_exceeded` beyond that); vehicle-side tag
2063
+ * lists do not count.
1996
2064
  */
1997
2065
  tags?: string[] | null;
1998
2066
  /**
@@ -2046,11 +2114,12 @@ export interface components {
2046
2114
  seed?: number | null;
2047
2115
  /**
2048
2116
  * Format: double
2049
- * @description Search time budget in seconds, fractions allowed (default 1). Maximum
2050
- * 300: a larger value is rejected with HTTP 400. A value below 0.1 is
2051
- * raised to a 100 ms floor. Pure search time: fetching travel times and
2052
- * waiting for capacity are not deducted from it, so the request takes
2053
- * longer than this value.
2117
+ * @description Search time budget in seconds, fractions allowed. Default when omitted:
2118
+ * 150 ms plus 4 ms per task (jobs and shipments combined), so 100 tasks
2119
+ * search for 0.55 s. Maximum 60: a larger value is rejected with HTTP 400.
2120
+ * A value below 0.1 is raised to a 100 ms floor. Pure search time:
2121
+ * fetching travel times and waiting for capacity are not deducted from it,
2122
+ * so the request takes longer than this value.
2054
2123
  * @example 2.5
2055
2124
  */
2056
2125
  time_limit_s?: number | null;
@@ -2294,7 +2363,8 @@ export interface components {
2294
2363
  * "target": 0,
2295
2364
  * "time": 57.1536,
2296
2365
  * "waiting": 0,
2297
- * "window_choice": 0
2366
+ * "window_choice": 0,
2367
+ * "workload_share": 0
2298
2368
  * },
2299
2369
  * "cost": 58.8436,
2300
2370
  * "currency": "EUR",
@@ -2380,7 +2450,8 @@ export interface components {
2380
2450
  * This is the nominal figure. The serving vehicle's `service_factor`
2381
2451
  * scales it — the time actually scheduled is
2382
2452
  * `service_duration_s / service_factor` — so the solution's
2383
- * `stops[].service_s` may differ from what you sent here.
2453
+ * `stops[].service_s` may differ from what you sent here. Default `0`;
2454
+ * a negative value is rejected with HTTP 400.
2384
2455
  * @example 300
2385
2456
  */
2386
2457
  service_duration_s?: number | null;
@@ -2393,6 +2464,7 @@ export interface components {
2393
2464
  *
2394
2465
  * Not scaled by the vehicle's `service_factor`: setup is fixed arrival
2395
2466
  * overhead (parking, walking to the door), not the work itself.
2467
+ * Default `0`; a negative value is rejected with HTTP 400.
2396
2468
  * @example 60
2397
2469
  */
2398
2470
  setup_duration_s?: number | null;
@@ -2517,8 +2589,8 @@ export interface components {
2517
2589
  * Format: date-time
2518
2590
  * @description When service starts (RFC 3339, in the offset of `arrival`): after the
2519
2591
  * arrival, any windowed
2520
- * break taken on arrival there, and the wait. On task and reload stops;
2521
- * like `arrival`, absent on a request without time windows.
2592
+ * break taken on arrival there, and the wait. Present on task and reload
2593
+ * stops; absent on start, end and break stops.
2522
2594
  * @example 2026-07-01T09:00:00+02:00
2523
2595
  */
2524
2596
  service_start?: string | null;
@@ -2576,15 +2648,19 @@ export interface components {
2576
2648
  */
2577
2649
  SuggestOptions: {
2578
2650
  /**
2579
- * @description Max ranked options per task. Default 5; `0` returns every feasible
2580
- * placement; positive values are clamped to `1..=100`.
2651
+ * @description Max ranked options per task. Default 5; positive values are clamped
2652
+ * to `1..=100`; `0` lifts the cap. It also bounds the search for a unit
2653
+ * of several stops, and that search is never exhaustive: even with `0`,
2654
+ * each stop after the unit's first keeps at most the 40 most promising
2655
+ * partial placements. A single-stop task has every feasible position
2656
+ * considered unless `time_limit_s` runs out first.
2581
2657
  * @example 5
2582
2658
  */
2583
2659
  max_suggestions?: number | null;
2584
2660
  /**
2585
2661
  * Format: double
2586
- * @description Search budget in seconds, fractions allowed (default 1, as on `/solve`;
2587
- * minimum 0.1 — smaller values are raised to it; maximum 300). The clock
2662
+ * @description Search budget in seconds, fractions allowed (default 1; minimum 0.1 —
2663
+ * smaller values are raised to it; maximum 60). The clock
2588
2664
  * starts when the search starts, after the matrix fetch and any wait for a
2589
2665
  * solver slot. Units are searched in problem order (jobs, then
2590
2666
  * shipments), each with an equal share of the time left when it starts; a
@@ -2788,7 +2864,8 @@ export interface components {
2788
2864
  * "target": 0,
2789
2865
  * "time": 57.1536,
2790
2866
  * "waiting": 0,
2791
- * "window_choice": 0
2867
+ * "window_choice": 0,
2868
+ * "workload_share": 0
2792
2869
  * },
2793
2870
  * "cost": 58.8436,
2794
2871
  * "currency": "EUR",
@@ -2849,7 +2926,8 @@ export interface components {
2849
2926
  * `score.violations` hold only against it, and no `routes[].polyline` is
2850
2927
  * returned. Solve again once Maps is back if the plan must hold on the
2851
2928
  * road; `options.travel_fallback: "fail"` returns the error instead.
2852
- * Absent when travel is road-routed.
2929
+ * Absent when travel is road-routed, and always absent on `/evaluate`,
2930
+ * which returns the error rather than estimate.
2853
2931
  */
2854
2932
  travel_estimated?: boolean;
2855
2933
  /**
@@ -2875,7 +2953,7 @@ export interface components {
2875
2953
  /**
2876
2954
  * Format: double
2877
2955
  * @description Cost, in the objective's currency, incurred when a task without this
2878
- * tag is served.
2956
+ * tag is served. Must be finite and `>= 0`.
2879
2957
  * @example 25
2880
2958
  */
2881
2959
  violation_cost: number;
@@ -2954,7 +3032,7 @@ export interface components {
2954
3032
  */
2955
3033
  on?: null | components["schemas"]["StopInstant"];
2956
3034
  /**
2957
- * @description A job id (shipment legs are not supported yet).
3035
+ * @description A job id. A shipment or a shipment leg is rejected with HTTP 400.
2958
3036
  * @example job-1
2959
3037
  */
2960
3038
  stop: string;
@@ -3041,7 +3119,8 @@ export interface components {
3041
3119
  * alternatives; it does not soften time-window feasibility. Must be
3042
3120
  * finite and `>= 0`, and requires at least 2 windows on the task. Once
3043
3121
  * any window on the task carries a *nonzero* cost, that task's windows
3044
- * must also be pairwise non-overlapping (a task whose costs are all
3122
+ * must also be pairwise non-overlapping, and windows that only touch
3123
+ * count as overlapping (a task whose costs are all
3045
3124
  * `0`/absent has its overlapping windows merged).
3046
3125
  *
3047
3126
  * **Cannot be combined with `objective.per_late_hour > 0`** — a
@@ -3125,8 +3204,8 @@ export interface components {
3125
3204
  engine?: null | components["schemas"]["MatrixEngine"];
3126
3205
  };
3127
3206
  /**
3128
- * @description [`SolveOptions::travel_fallback`]: what a solve does when travel times
3129
- * cannot be fetched.
3207
+ * @description `options.travel_fallback`: what a solve does when travel times cannot be
3208
+ * fetched. `/evaluate` and `/suggest` never estimate: they fail instead.
3130
3209
  * @example estimate
3131
3210
  * @enum {string}
3132
3211
  */
@@ -3323,7 +3402,10 @@ export interface components {
3323
3402
  * day with `shifts[].service_factor`.
3324
3403
  */
3325
3404
  service_factor?: number | null;
3326
- /** @description One or more working shifts. */
3405
+ /**
3406
+ * @description One or more working shifts. A vehicle without shifts is rejected with
3407
+ * HTTP 400.
3408
+ */
3327
3409
  shifts: components["schemas"]["Shift"][];
3328
3410
  /**
3329
3411
  * @description Capabilities the vehicle provides. A job's `skills` must all appear
@@ -3345,6 +3427,16 @@ export interface components {
3345
3427
  * range.
3346
3428
  */
3347
3429
  speed_factor?: number | null;
3430
+ /**
3431
+ * Format: double
3432
+ * @description Share of the work this vehicle carries under `objective.workload_share`,
3433
+ * relative to the other vehicles. Default `1`; `0.5` carries half as much;
3434
+ * `0` leaves the vehicle out of the balancing. `0` to `1000000`
3435
+ * (HTTP 400 `out_of_range` outside that). Ignored when
3436
+ * `objective.workload_share` is not set.
3437
+ * @example 0.5
3438
+ */
3439
+ workload_weight?: number | null;
3348
3440
  };
3349
3441
  /**
3350
3442
  * @description Vehicle distance/time limits.
@@ -3356,14 +3448,15 @@ export interface components {
3356
3448
  VehicleLimits: {
3357
3449
  /**
3358
3450
  * Format: int64
3359
- * @description Hard maximum distance in metres.
3451
+ * @description Hard maximum distance in metres. `0` to `2147483647`; a value
3452
+ * outside that range is rejected with HTTP 400 `out_of_range`.
3360
3453
  * @example 200000
3361
3454
  */
3362
3455
  max_distance_m?: number | null;
3363
3456
  /**
3364
3457
  * Format: int64
3365
3458
  * @description Hard cap in seconds on any single travel leg of this vehicle's routes:
3366
- * the `travel_time_s` reported on each stop, start-to-first-task and
3459
+ * the `travel_s` reported on each stop, start-to-first-task and
3367
3460
  * last-task-to-`end` included. Travel only: setup, service and wait do
3368
3461
  * not count, and `speed_factor` applies first. A leg made free by
3369
3462
  * `ignore_start_leg`/`ignore_end_leg`, or absent on an open start or
@@ -3377,7 +3470,9 @@ export interface components {
3377
3470
  * Format: int64
3378
3471
  * @description Hard maximum route duration in seconds: the whole-route span —
3379
3472
  * travel, service, and wait all count, depot load and unload included.
3380
- * The same quantity as the response's `routes[].time.span_s`.
3473
+ * The same quantity as the response's `routes[].time.span_s`. `0` to
3474
+ * `2147483647`; a value outside that range is rejected with HTTP 400
3475
+ * `out_of_range`.
3381
3476
  * @example 32400
3382
3477
  */
3383
3478
  max_route_duration_s?: number | null;
@@ -3440,8 +3535,9 @@ export interface components {
3440
3535
  * @description Machine-readable violation type: `time_window`, `capacity`, `skills`,
3441
3536
  * `tags`, `first_job`, `unreachable`, `commitment`, `eligibility`,
3442
3537
  * `same_vehicle`, `same_day`, `same_route`, `synchronized`, `time_lag`,
3443
- * `max_distance`, `max_duration`, `shift_end`, `sequence`, `max_tasks`,
3444
- * `break`, `shipment`, `multi_trip`, `period_limit` (a `limits.periods`
3538
+ * `compatible_vehicles`, `max_distance`, `max_duration`, `shift_end`,
3539
+ * `sequence`, `max_tasks`, `break`, `shipment`, `multi_trip`,
3540
+ * `period_limit` (a `limits.periods`
3445
3541
  * cap, reported once per period on the route of its first used shift),
3446
3542
  * `quota` (a `quotas[]` cap, reported the same way), or `infeasible`
3447
3543
  * (the insertion check refused a stop without naming a constraint; not
@@ -3517,10 +3613,17 @@ export interface components {
3517
3613
  * `score.components.drop_fees` and summed into `score.cost`.
3518
3614
  *
3519
3615
  * On a shipment the fee applies to the pair, charged once.
3616
+ *
3617
+ * Must be finite, `>= 0` and at most `1e9`; combined with `priority` it
3618
+ * is rejected with HTTP 400 (`conflicting_fields`).
3520
3619
  * @example 25
3521
3620
  */
3522
3621
  drop_fee?: number | null;
3523
- /** @description Hard vehicle allow/exclude. */
3622
+ /**
3623
+ * @description Hard vehicle allow/exclude. A request may use at most 256 distinct
3624
+ * permitted-vehicle sets (after resolving `allowed`/`excluded`) across
3625
+ * all jobs and shipments (HTTP 400 `limit_exceeded` beyond that).
3626
+ */
3524
3627
  eligible_vehicles?: null | components["schemas"]["Eligibility"];
3525
3628
  /**
3526
3629
  * @description Job id: 1 to 128 characters, unique across `jobs` + `shipments`.
@@ -3608,7 +3711,8 @@ export interface components {
3608
3711
  * This is the nominal figure. The serving vehicle's `service_factor`
3609
3712
  * scales it — the time actually scheduled is
3610
3713
  * `service_duration_s / service_factor` — so the solution's
3611
- * `stops[].service_s` may differ from what you sent here.
3714
+ * `stops[].service_s` may differ from what you sent here. Default `0`;
3715
+ * a negative value is rejected with HTTP 400.
3612
3716
  * @example 300
3613
3717
  */
3614
3718
  service_duration_s?: number | null;
@@ -3628,12 +3732,15 @@ export interface components {
3628
3732
  *
3629
3733
  * Not scaled by the vehicle's `service_factor`: setup is fixed arrival
3630
3734
  * overhead (parking, walking to the door), not the work itself.
3735
+ * Default `0`; a negative value is rejected with HTTP 400.
3631
3736
  * @example 60
3632
3737
  */
3633
3738
  setup_duration_s?: number | null;
3634
3739
  /**
3635
3740
  * @description Hard skill requirements — the assigned vehicle must provide every one,
3636
- * at or above each requirement's `min_level`.
3741
+ * at or above each requirement's `min_level`. A request may use at most
3742
+ * 256 distinct `(name, min_level)` pairs across all jobs and shipments
3743
+ * (HTTP 400 `limit_exceeded` beyond that).
3637
3744
  */
3638
3745
  skills?: components["schemas"]["SkillReq"][] | null;
3639
3746
  /**
@@ -3642,6 +3749,9 @@ export interface components {
3642
3749
  * `serves_tags` (hard ANY-overlap) or a vehicle prices them via
3643
3750
  * `preferred_tags` (soft), for tag matching. ANY-overlap — contrast with
3644
3751
  * `skills`, which requires ALL. Not capability matching on its own.
3752
+ * A request may carry at most 256 distinct tags across all jobs and
3753
+ * shipments (HTTP 400 `limit_exceeded` beyond that); vehicle-side tag
3754
+ * lists do not count.
3645
3755
  */
3646
3756
  tags?: string[] | null;
3647
3757
  /**
@@ -3652,6 +3762,46 @@ export interface components {
3652
3762
  /** @description Time windows for this visit. */
3653
3763
  time_windows?: components["schemas"]["TimeWindow"][] | null;
3654
3764
  };
3765
+ /**
3766
+ * @description Spreads work across vehicles: each vehicle's scheduled service time is
3767
+ * compared with its share of all the work, and the part outside a tolerance
3768
+ * band is charged.
3769
+ *
3770
+ * The balanced quantity is **service time only**: every served task's
3771
+ * `service_duration_s`, scaled by the vehicle's `service_factor`. Travel,
3772
+ * `setup_duration_s`, waiting and breaks are not counted, and a task-level
3773
+ * `service_factors` entry scales the measure but not the target. Shares are
3774
+ * fixed when the request is read: every task counts towards the total,
3775
+ * served or not, so with many unassigned tasks every vehicle sits under its
3776
+ * share and the term pulls work in without ordering the routes between them;
3777
+ * balancing pressure appears once routes enter their band. A vehicle with
3778
+ * several shifts has its share split across them by the work each shift can
3779
+ * reach (the service time of the tasks whose windows overlap it), weighted
3780
+ * by shift length, so a day with no tasks carries none of the share; each
3781
+ * shift is charged on its own part. An empty, non-exempt shift pays for its whole share, so pair the
3782
+ * term with `costs.fixed` or `per_vehicle` only when both are meant.
3783
+ * See `vehicles[].workload_weight` to give a part-time vehicle a smaller
3784
+ * share. A problem that sets the term solves without decomposition, like
3785
+ * `assignment_costs`.
3786
+ */
3787
+ WorkloadShare: {
3788
+ /**
3789
+ * Format: double
3790
+ * @description Money per hour a vehicle's service time is outside the band, in the
3791
+ * request's currency. Greater than 0 and at least 0.036 (the internal
3792
+ * resolution); omit `workload_share` to switch the term off.
3793
+ * @example 35
3794
+ */
3795
+ per_imbalance_hour: number;
3796
+ /**
3797
+ * Format: double
3798
+ * @description Half-width of the band as a fraction of the vehicle's share, `0` up to
3799
+ * but not including `1`. Default `0.1`: a vehicle within 10% of its share
3800
+ * is not charged. The charge grows from the band edge.
3801
+ * @example 0.1
3802
+ */
3803
+ tolerance?: number | null;
3804
+ };
3655
3805
  };
3656
3806
  responses: never;
3657
3807
  parameters: never;
@@ -3667,7 +3817,12 @@ export interface operations {
3667
3817
  /**
3668
3818
  * @description Check the request without fetching a matrix or searching. 200
3669
3819
  * `{"valid": true, "warnings": []}`, or a 400 for the problem, options
3670
- * and caps; `plan` is checked only by the real call, after the fetch.
3820
+ * and caps; `plan` is matched against the problem only by the real call,
3821
+ * after the fetch (suggest's check that `plan` leaves a task to place
3822
+ * does run here).
3823
+ * Any other query parameter is rejected with a 400 `unknown_field`, as in
3824
+ * the body: a misspelt `validateOnly` must not fall through to a billed
3825
+ * solve.
3671
3826
  */
3672
3827
  validate_only?: boolean;
3673
3828
  };
@@ -3692,7 +3847,7 @@ export interface operations {
3692
3847
  "application/json": components["schemas"]["OrValidateOnly_SolveResponse"];
3693
3848
  };
3694
3849
  };
3695
- /** @description The request is invalid. `errors[]` lists what to fix, each entry with a `code` and a JSON pointer into the request body. */
3850
+ /** @description The request is invalid. `errors[]` lists what to fix, each entry with a `code` and, when the problem is in the body, a JSON `pointer` to it. */
3696
3851
  400: {
3697
3852
  headers: {
3698
3853
  /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
@@ -3710,7 +3865,7 @@ export interface operations {
3710
3865
  };
3711
3866
  content?: never;
3712
3867
  };
3713
- /** @description The request uses a feature this account does not have (`tomtom_real_time`) */
3868
+ /** @description The request uses a feature this account does not have (`tomtom_real_time`). Also returned by `?validate_only=true`. */
3714
3869
  403: {
3715
3870
  headers: {
3716
3871
  /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
@@ -3729,7 +3884,7 @@ export interface operations {
3729
3884
  "application/json": components["schemas"]["ErrorBody"];
3730
3885
  };
3731
3886
  };
3732
- /** @description Request body larger than 10 MiB */
3887
+ /** @description Request body larger than 10 MiB (10,485,760 bytes), counted after gzip decompression */
3733
3888
  413: {
3734
3889
  headers: {
3735
3890
  /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
@@ -3748,7 +3903,7 @@ export interface operations {
3748
3903
  "application/json": components["schemas"]["ErrorBody"];
3749
3904
  };
3750
3905
  };
3751
- /** @description Request `Content-Type` is not `application/json` */
3906
+ /** @description Request `Content-Type` is missing or not JSON (`application/json` or `application/*+json`), or the body has a `Content-Encoding` other than `gzip` (the message says which) */
3752
3907
  415: {
3753
3908
  headers: {
3754
3909
  /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
@@ -3785,7 +3940,7 @@ export interface operations {
3785
3940
  "application/json": components["schemas"]["ErrorBody"];
3786
3941
  };
3787
3942
  };
3788
- /** @description Travel times and distances could not be fetched (`code: matrix_provider_error`). The request can be retried. */
3943
+ /** @description Travel times and distances could not be fetched (`code: matrix_provider_error`). The request can be retried. This endpoint never falls back to estimated travel. */
3789
3944
  502: {
3790
3945
  headers: {
3791
3946
  /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
@@ -3796,7 +3951,7 @@ export interface operations {
3796
3951
  "application/json": components["schemas"]["ErrorBody"];
3797
3952
  };
3798
3953
  };
3799
- /** @description No capacity right now (`code: overloaded`). Retry after the number of seconds in the `Retry-After` header. */
3954
+ /** @description Solvice Maps is temporarily refused after repeated failures (`code: overloaded`), so travel times cannot be fetched. Evaluate takes no solver slot, so this is its only 503. Retry after the number of seconds in the `Retry-After` header. */
3800
3955
  503: {
3801
3956
  headers: {
3802
3957
  /** @description Seconds to wait before retrying. */
@@ -3809,7 +3964,7 @@ export interface operations {
3809
3964
  /**
3810
3965
  * @example {
3811
3966
  * "code": "overloaded",
3812
- * "message": "All solver slots are busy, try again later",
3967
+ * "message": "Circuit breaker is open — Solvice Maps temporarily unavailable",
3813
3968
  * "request_id": "req_7f3a2c90d1e4b5a6",
3814
3969
  * "status": "SERVICE_UNAVAILABLE"
3815
3970
  * }
@@ -3825,7 +3980,12 @@ export interface operations {
3825
3980
  /**
3826
3981
  * @description Check the request without fetching a matrix or searching. 200
3827
3982
  * `{"valid": true, "warnings": []}`, or a 400 for the problem, options
3828
- * and caps; `plan` is checked only by the real call, after the fetch.
3983
+ * and caps; `plan` is matched against the problem only by the real call,
3984
+ * after the fetch (suggest's check that `plan` leaves a task to place
3985
+ * does run here).
3986
+ * Any other query parameter is rejected with a 400 `unknown_field`, as in
3987
+ * the body: a misspelt `validateOnly` must not fall through to a billed
3988
+ * solve.
3829
3989
  */
3830
3990
  validate_only?: boolean;
3831
3991
  };
@@ -3850,7 +4010,7 @@ export interface operations {
3850
4010
  "application/json": components["schemas"]["OrValidateOnly_SolveResponse"];
3851
4011
  };
3852
4012
  };
3853
- /** @description The request is invalid. `errors[]` lists what to fix, each entry with a `code` and a JSON pointer into the request body. */
4013
+ /** @description The request is invalid. `errors[]` lists what to fix, each entry with a `code` and, when the problem is in the body, a JSON `pointer` to it. */
3854
4014
  400: {
3855
4015
  headers: {
3856
4016
  /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
@@ -3868,7 +4028,7 @@ export interface operations {
3868
4028
  };
3869
4029
  content?: never;
3870
4030
  };
3871
- /** @description The request uses a feature this account does not have (`tomtom_real_time`) */
4031
+ /** @description The request uses a feature this account does not have (`tomtom_real_time`). Also returned by `?validate_only=true`. */
3872
4032
  403: {
3873
4033
  headers: {
3874
4034
  /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
@@ -3887,7 +4047,7 @@ export interface operations {
3887
4047
  "application/json": components["schemas"]["ErrorBody"];
3888
4048
  };
3889
4049
  };
3890
- /** @description Request body larger than 10 MiB */
4050
+ /** @description Request body larger than 10 MiB (10,485,760 bytes), counted after gzip decompression */
3891
4051
  413: {
3892
4052
  headers: {
3893
4053
  /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
@@ -3906,7 +4066,7 @@ export interface operations {
3906
4066
  "application/json": components["schemas"]["ErrorBody"];
3907
4067
  };
3908
4068
  };
3909
- /** @description Request `Content-Type` is not `application/json` */
4069
+ /** @description Request `Content-Type` is missing or not JSON (`application/json` or `application/*+json`), or the body has a `Content-Encoding` other than `gzip` (the message says which) */
3910
4070
  415: {
3911
4071
  headers: {
3912
4072
  /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
@@ -3954,7 +4114,7 @@ export interface operations {
3954
4114
  "application/json": components["schemas"]["ErrorBody"];
3955
4115
  };
3956
4116
  };
3957
- /** @description No capacity right now (`code: overloaded`). Retry after the number of seconds in the `Retry-After` header. */
4117
+ /** @description No capacity right now (`code: overloaded`): every solver slot is busy, or the wait for one timed out. With `options.travel_fallback: "fail"`, also returned while Solvice Maps is temporarily refused after repeated failures. Retry after the number of seconds in the `Retry-After` header. */
3958
4118
  503: {
3959
4119
  headers: {
3960
4120
  /** @description Seconds to wait before retrying. */
@@ -3983,7 +4143,12 @@ export interface operations {
3983
4143
  /**
3984
4144
  * @description Check the request without fetching a matrix or searching. 200
3985
4145
  * `{"valid": true, "warnings": []}`, or a 400 for the problem, options
3986
- * and caps; `plan` is checked only by the real call, after the fetch.
4146
+ * and caps; `plan` is matched against the problem only by the real call,
4147
+ * after the fetch (suggest's check that `plan` leaves a task to place
4148
+ * does run here).
4149
+ * Any other query parameter is rejected with a 400 `unknown_field`, as in
4150
+ * the body: a misspelt `validateOnly` must not fall through to a billed
4151
+ * solve.
3987
4152
  */
3988
4153
  validate_only?: boolean;
3989
4154
  };
@@ -4008,7 +4173,7 @@ export interface operations {
4008
4173
  "application/json": components["schemas"]["OrValidateOnly_SuggestResponse"];
4009
4174
  };
4010
4175
  };
4011
- /** @description The request is invalid. `errors[]` lists what to fix, each entry with a `code` and a JSON pointer into the request body. */
4176
+ /** @description The request is invalid. `errors[]` lists what to fix, each entry with a `code` and, when the problem is in the body, a JSON `pointer` to it. */
4012
4177
  400: {
4013
4178
  headers: {
4014
4179
  /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
@@ -4026,7 +4191,7 @@ export interface operations {
4026
4191
  };
4027
4192
  content?: never;
4028
4193
  };
4029
- /** @description The request uses a feature this account does not have (`tomtom_real_time`) */
4194
+ /** @description The request uses a feature this account does not have (`tomtom_real_time`). Also returned by `?validate_only=true`. */
4030
4195
  403: {
4031
4196
  headers: {
4032
4197
  /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
@@ -4045,7 +4210,7 @@ export interface operations {
4045
4210
  "application/json": components["schemas"]["ErrorBody"];
4046
4211
  };
4047
4212
  };
4048
- /** @description Request body larger than 10 MiB */
4213
+ /** @description Request body larger than 10 MiB (10,485,760 bytes), counted after gzip decompression */
4049
4214
  413: {
4050
4215
  headers: {
4051
4216
  /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
@@ -4064,7 +4229,7 @@ export interface operations {
4064
4229
  "application/json": components["schemas"]["ErrorBody"];
4065
4230
  };
4066
4231
  };
4067
- /** @description Request `Content-Type` is not `application/json` */
4232
+ /** @description Request `Content-Type` is missing or not JSON (`application/json` or `application/*+json`), or the body has a `Content-Encoding` other than `gzip` (the message says which) */
4068
4233
  415: {
4069
4234
  headers: {
4070
4235
  /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
@@ -4101,7 +4266,7 @@ export interface operations {
4101
4266
  "application/json": components["schemas"]["ErrorBody"];
4102
4267
  };
4103
4268
  };
4104
- /** @description Travel times and distances could not be fetched (`code: matrix_provider_error`). The request can be retried. */
4269
+ /** @description Travel times and distances could not be fetched (`code: matrix_provider_error`). The request can be retried. This endpoint never falls back to estimated travel. */
4105
4270
  502: {
4106
4271
  headers: {
4107
4272
  /** @description The request's `X-Request-Id`, echoed, or a generated `req_…` id. */
@@ -4112,7 +4277,7 @@ export interface operations {
4112
4277
  "application/json": components["schemas"]["ErrorBody"];
4113
4278
  };
4114
4279
  };
4115
- /** @description No capacity right now (`code: overloaded`). Retry after the number of seconds in the `Retry-After` header. */
4280
+ /** @description No capacity right now (`code: overloaded`): every solver slot is busy, the wait for one timed out, or Solvice Maps is temporarily refused after repeated failures. Retry after the number of seconds in the `Retry-After` header. */
4116
4281
  503: {
4117
4282
  headers: {
4118
4283
  /** @description Seconds to wait before retrying. */
package/package.json CHANGED
@@ -1,9 +1,14 @@
1
1
  {
2
2
  "name": "@solvice/routing",
3
- "version": "0.1.0-beta.0",
3
+ "version": "0.1.0-beta.1",
4
4
  "description": "TypeScript client for the Solvice V3 routing API, generated from its OpenAPI spec.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://docs.solvice.io/guides/platform/sdks/typescript",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/solvice/solvice.git",
10
+ "directory": "sdk/typescript"
11
+ },
7
12
  "publishConfig": {
8
13
  "access": "public"
9
14
  },