@solvice/routing 0.17.2-beta.0 → 0.17.4-beta.0

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
@@ -70,6 +70,36 @@ try {
70
70
  | `evaluate(request)` | `POST /v3/routing/evaluate` |
71
71
  | `suggest(request)` | `POST /v3/routing/suggest` |
72
72
  | `validate.solve(request)`, `validate.evaluate(request)`, `validate.suggest(request)` | the same three sent to `/v3/routing/validate/solve`, `/evaluate`, `/suggest`: checked, not run |
73
+ | `runs.submit(request)` | `POST /v3/routing/runs`: answers at once with a `queued` run |
74
+ | `runs.get(id)` | `GET /v3/routing/runs/{id}`: poll the run's `status` |
75
+ | `runs.result(id)` | `GET /v3/routing/runs/{id}/result`: the plan of a `succeeded` run |
76
+ | `runs.cancel(id)` | `POST /v3/routing/runs/{id}/cancel` |
77
+
78
+ An async run takes the `solve` body and may search for up to 3,600 s
79
+ (`options.time_limit_s`). Submit it, poll until `status` is `succeeded`, `failed` or
80
+ `cancelled`, then read the plan:
81
+
82
+ ```typescript
83
+ import { createSolvice, type SolveRequest } from '@solvice/routing';
84
+
85
+ const solvice = createSolvice({ apiKey: process.env.SOLVICE_API_KEY! });
86
+
87
+ export async function solveInBackground(problem: SolveRequest['problem']) {
88
+ let run = await solvice.runs.submit({ problem, options: { time_limit_s: 600 } });
89
+ while (run.status === 'queued' || run.status === 'running') {
90
+ await new Promise((resolve) => setTimeout(resolve, 5000));
91
+ run = await solvice.runs.get(run.id);
92
+ }
93
+ if (run.status !== 'succeeded') {
94
+ throw new Error(`run ${run.id} ${run.status}: ${run.error?.message ?? 'no plan'}`);
95
+ }
96
+ return solvice.runs.result(run.id);
97
+ }
98
+ ```
99
+
100
+ The run endpoints are served by the API gateway, so their `404` and `409` errors are
101
+ not the API's error body: a `SolviceError` with no `body`, and `raw.message` starting
102
+ with a code (`not_found`, `run_not_finished`, `no_result`).
73
103
 
74
104
  Any non-2xx response throws a `SolviceError` with the HTTP `status`, the
75
105
  `response`, and, when the response carried one, the API's error `body`. A `401`
@@ -90,10 +120,10 @@ Documentation: <https://docs.solvice.io>
90
120
 
91
121
  ## Development
92
122
 
93
- `src/v3.ts` is generated; do not edit it. After an API change:
123
+ `src/v3.ts` and `src/runs.ts` are generated; do not edit them. After an API change:
94
124
 
95
125
  ```bash
96
- npm run generate # from mintlify/openapi.json
126
+ npm run generate # v3.ts from mintlify/openapi.json, runs.ts from mintlify/openapi-runs.json
97
127
  npm test # builds, typechecks tests/types.ts and examples/, runs the unit tests and the solve-response fixture check
98
128
  npm run smoke # live calls; needs SOLVICE_API_KEY
99
129
  ```
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
+ import type { components as runsComponents, paths as runsPaths } from "./runs.js";
1
2
  import type { components, paths } from "./v3.js";
2
- export type { components, paths };
3
+ export type { components, paths, runsComponents, runsPaths };
3
4
  export type Schemas = components["schemas"];
4
5
  export type SolveRequest = Schemas["SolveRequest"];
5
6
  export type EvaluateRequest = Schemas["EvaluateRequest"];
@@ -8,6 +9,12 @@ export type SolveResponse = Schemas["SolveResponse"];
8
9
  export type SuggestResponse = Schemas["SuggestResponse"];
9
10
  export type ValidateResponse = Schemas["ValidateResponse"];
10
11
  export type ErrorBody = Schemas["ErrorBody"];
12
+ export type RunSchemas = runsComponents["schemas"];
13
+ export type RunRequest = RunSchemas["RunRequest"];
14
+ export type Run = RunSchemas["Run"];
15
+ export type RunStatus = RunSchemas["RunStatus"];
16
+ /** The gateway's own error answer on `/v3/routing/runs*`: not an `ErrorBody`. */
17
+ export type GatewayError = RunSchemas["GatewayError"];
11
18
  export interface SolviceOptions {
12
19
  /** Sent as the `Authorization` header value, with no scheme prefix. */
13
20
  apiKey: string;
@@ -51,6 +58,22 @@ export declare function createSolvice({ apiKey, baseUrl, fetch }: SolviceOptions
51
58
  evaluate: (body: EvaluateRequest, options?: RequestOptions) => Promise<ValidateResponse>;
52
59
  suggest: (body: SuggestRequest, options?: RequestOptions) => Promise<ValidateResponse>;
53
60
  };
61
+ /**
62
+ * Async runs, served by the API gateway: `submit` answers at once and the
63
+ * search runs in the background for up to `options.time_limit_s` (3,600 s).
64
+ * Poll `get` until `status` is `succeeded`, `failed` or `cancelled`, then
65
+ * read the plan with `result`. A gateway error (`404`, `409`) is a
66
+ * `SolviceError` with no `body`; its `raw.message` starts with a code such
67
+ * as `not_found`, `run_not_finished` or `no_result`.
68
+ */
69
+ runs: {
70
+ submit: (body: RunRequest, options?: RequestOptions) => Promise<Run>;
71
+ get: (id: string, options?: RequestOptions) => Promise<Run>;
72
+ /** The plan of a `succeeded` run; a run that is not finished, failed or cancelled answers `409`. */
73
+ result: (id: string, options?: RequestOptions) => Promise<SolveResponse>;
74
+ /** Cancels a `queued` or `running` run; a finished run comes back in its final state. */
75
+ cancel: (id: string, options?: RequestOptions) => Promise<Run>;
76
+ };
54
77
  /** The underlying openapi-fetch client, for anything the methods above do not cover. */
55
- client: import("openapi-fetch").Client<paths, `${string}/${string}`>;
78
+ client: import("openapi-fetch").Client<paths & runsPaths, `${string}/${string}`>;
56
79
  };
package/dist/index.js CHANGED
@@ -34,7 +34,15 @@ export class SolviceError extends Error {
34
34
  const isRecord = (value) => typeof value === "object" && value !== null;
35
35
  /** The API's error body has `status`, `code` and `message`; the gateway's `401` has `message` and `error`. */
36
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;
37
+ const gatewayMessage = (raw) => {
38
+ if (!isRecord(raw))
39
+ return undefined;
40
+ if (typeof raw.message === "string")
41
+ return raw.message;
42
+ // A malformed run id answers `{ error: "Invalid UUID string: ..." }` with no `message`;
43
+ // the bare `GATEWAY_ERROR` constant says less than the status line does.
44
+ return typeof raw.error === "string" && raw.error !== "GATEWAY_ERROR" ? raw.error : undefined;
45
+ };
38
46
  function unwrap({ data, error, response }) {
39
47
  if (!response.ok) {
40
48
  // openapi-fetch hands back the raw text when the error is not JSON, and "" for no body.
@@ -55,6 +63,22 @@ export function createSolvice({ apiKey, baseUrl = "https://api.solvice.io", fetc
55
63
  evaluate: async (body, options) => unwrap(await client.POST("/v3/routing/validate/evaluate", { body, ...options })),
56
64
  suggest: async (body, options) => unwrap(await client.POST("/v3/routing/validate/suggest", { body, ...options })),
57
65
  },
66
+ /**
67
+ * Async runs, served by the API gateway: `submit` answers at once and the
68
+ * search runs in the background for up to `options.time_limit_s` (3,600 s).
69
+ * Poll `get` until `status` is `succeeded`, `failed` or `cancelled`, then
70
+ * read the plan with `result`. A gateway error (`404`, `409`) is a
71
+ * `SolviceError` with no `body`; its `raw.message` starts with a code such
72
+ * as `not_found`, `run_not_finished` or `no_result`.
73
+ */
74
+ runs: {
75
+ submit: async (body, options) => unwrap(await client.POST("/v3/routing/runs", { body, ...options })),
76
+ get: async (id, options) => unwrap(await client.GET("/v3/routing/runs/{id}", { params: { path: { id } }, ...options })),
77
+ /** The plan of a `succeeded` run; a run that is not finished, failed or cancelled answers `409`. */
78
+ result: async (id, options) => unwrap(await client.GET("/v3/routing/runs/{id}/result", { params: { path: { id } }, ...options })),
79
+ /** Cancels a `queued` or `running` run; a finished run comes back in its final state. */
80
+ cancel: async (id, options) => unwrap(await client.POST("/v3/routing/runs/{id}/cancel", { params: { path: { id } }, ...options })),
81
+ },
58
82
  /** The underlying openapi-fetch client, for anything the methods above do not cover. */
59
83
  client,
60
84
  };