@typeship-ax/cli 0.20.0 → 0.21.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/AGENTS.md +3 -3
- package/README.md +2 -2
- package/api.json +7355 -6233
- package/api.md +332 -398
- package/dist/api-identity.d.ts.map +1 -1
- package/dist/api-identity.js +6 -1
- package/dist/cli.js +15 -15
- package/dist/core/http.d.ts +26 -14
- package/dist/core/http.d.ts.map +1 -1
- package/dist/core/http.js +90 -19
- package/dist/core/pagination.d.ts +8 -8
- package/dist/core/pagination.d.ts.map +1 -1
- package/dist/core/pagination.js +7 -16
- package/dist/errors.d.ts +4 -4
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +7 -7
- package/dist/index.d.ts +33 -19
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +37 -17
- package/dist/ops.d.ts +1 -1
- package/dist/ops.d.ts.map +1 -1
- package/dist/ops.js +39 -42
- package/dist/resources/api-keys.d.ts +10 -10
- package/dist/resources/api-keys.d.ts.map +1 -1
- package/dist/resources/api-keys.js +6 -6
- package/dist/resources/deliveries.d.ts +46 -0
- package/dist/resources/deliveries.d.ts.map +1 -0
- package/dist/resources/deliveries.js +70 -0
- package/dist/resources/drafts.d.ts +155 -0
- package/dist/resources/drafts.d.ts.map +1 -0
- package/dist/resources/drafts.js +230 -0
- package/dist/resources/files.d.ts +23 -0
- package/dist/resources/files.d.ts.map +1 -0
- package/dist/resources/files.js +38 -0
- package/dist/resources/generate.d.ts +14 -14
- package/dist/resources/generate.d.ts.map +1 -1
- package/dist/resources/generate.js +7 -7
- package/dist/resources/generations.d.ts +65 -21
- package/dist/resources/generations.d.ts.map +1 -1
- package/dist/resources/generations.js +67 -20
- package/dist/resources/organization.d.ts +18 -0
- package/dist/resources/organization.d.ts.map +1 -0
- package/dist/resources/{account.js → organization.js} +10 -10
- package/dist/resources/projects.d.ts +36 -126
- package/dist/resources/projects.d.ts.map +1 -1
- package/dist/resources/projects.js +21 -171
- package/dist/resources/publications.d.ts +46 -0
- package/dist/resources/publications.d.ts.map +1 -0
- package/dist/resources/publications.js +70 -0
- package/dist/resources/releases.d.ts +66 -0
- package/dist/resources/releases.d.ts.map +1 -0
- package/dist/resources/releases.js +101 -0
- package/dist/resources/spec-revisions.d.ts +85 -0
- package/dist/resources/spec-revisions.d.ts.map +1 -0
- package/dist/resources/spec-revisions.js +116 -0
- package/dist/resources/specs.d.ts +72 -0
- package/dist/resources/specs.d.ts.map +1 -0
- package/dist/resources/specs.js +107 -0
- package/dist/resources/targets.d.ts +42 -262
- package/dist/resources/targets.d.ts.map +1 -1
- package/dist/resources/targets.js +28 -407
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +149 -154
- package/dist/types.d.ts +1281 -1611
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +44 -49
- package/package.json +1 -1
- package/src/api-identity.ts +6 -2
- package/src/cli.ts +16 -16
- package/src/core/http.ts +89 -24
- package/src/core/pagination.ts +13 -23
- package/src/errors.ts +7 -7
- package/src/index.ts +41 -23
- package/src/ops.ts +40 -43
- package/src/resources/api-keys.ts +13 -16
- package/src/resources/deliveries.ts +139 -0
- package/src/resources/drafts.ts +422 -0
- package/src/resources/files.ts +68 -0
- package/src/resources/generate.ts +16 -19
- package/src/resources/generations.ts +146 -35
- package/src/resources/{account.ts → organization.ts} +14 -14
- package/src/resources/projects.ts +42 -337
- package/src/resources/publications.ts +139 -0
- package/src/resources/releases.ts +199 -0
- package/src/resources/spec-revisions.ts +237 -0
- package/src/resources/specs.ts +200 -0
- package/src/resources/targets.ts +57 -760
- package/src/schemas.ts +149 -154
- package/src/types.ts +1357 -1692
- package/dist/resources/account.d.ts +0 -18
- package/dist/resources/account.d.ts.map +0 -1
- package/dist/resources/definition-revisions.d.ts +0 -75
- package/dist/resources/definition-revisions.d.ts.map +0 -1
- package/dist/resources/definition-revisions.js +0 -142
- package/dist/resources/definitions.d.ts +0 -50
- package/dist/resources/definitions.d.ts.map +0 -1
- package/dist/resources/definitions.js +0 -73
- package/src/resources/definition-revisions.ts +0 -265
- package/src/resources/definitions.ts +0 -146
package/src/cli.ts
CHANGED
|
@@ -21,7 +21,7 @@ import { homedir, hostname } from "node:os";
|
|
|
21
21
|
import { basename, dirname, join } from "node:path";
|
|
22
22
|
import { fileURLToPath } from "node:url";
|
|
23
23
|
import { TypeshipClient, formatDebugEvent, type ClientOptions, type DebugEvent } from "./index.js";
|
|
24
|
-
import { validateAgainstSchema, ValidationError, type Violation } from "./core/http.js";
|
|
24
|
+
import { asApiResult, validateAgainstSchema, ValidationError, type Violation } from "./core/http.js";
|
|
25
25
|
import { SCHEMAS, DEFS } from "./schemas.js";
|
|
26
26
|
import { GLOBALS, OMITTED_OPS, OPS, buildArgs, findOp, missingRequired, type OmittedOpSpec, type OpSpec, type ParamSpec } from "./ops.js";
|
|
27
27
|
import {
|
|
@@ -42,17 +42,17 @@ const BASIC: { envUser: string; envPass: string } | null = null;
|
|
|
42
42
|
const EXCLUDED_OPS = 0;
|
|
43
43
|
/** Generated CLI operations that are intentionally unavailable to MCP. */
|
|
44
44
|
const MCP_EXCLUDED_OPS = 0;
|
|
45
|
-
const VERSION = "0.
|
|
45
|
+
const VERSION = "0.21.0";
|
|
46
46
|
const API_VERSION = "1.0.0";
|
|
47
47
|
const SPEC_FORMAT = "openapi";
|
|
48
48
|
const IDENTITY_POLICY: IdentityPolicy = {};
|
|
49
49
|
let LOGIN_IDENTITY: VerifiedIdentity | undefined;
|
|
50
|
-
const WHOAMI: { resource: string; method: string } | null =
|
|
50
|
+
const WHOAMI: { resource: string; method: string } | null = null;
|
|
51
51
|
const ENVIRONMENTS: Record<string, string> = {};
|
|
52
52
|
const HAS_MCP = false;
|
|
53
53
|
const PKG_NAME = "@typeship-ax/cli";
|
|
54
54
|
const UPDATE_NOTICE = false;
|
|
55
|
-
const API_DESCRIPTION: string | null = "Resolve an OpenAPI or GraphQL
|
|
55
|
+
const API_DESCRIPTION: string | null = "Resolve an OpenAPI or GraphQL Spec, diagnose it, and keep every\nselected CLI, MCP, and SDK Target current.\n\nEvery operation but one requires a bearer credential: an organization\nAPI key from the console, or an OAuth access token carrying the operation's\nread, generate, or write capability and the organization selected during\nconsent. OAuth grants cannot switch organizations after consent. A browser\nsession is not a credential for this API. The exception is POST /generate,\nwhich works anonymously with the free plan's limits.\n\nExamples use Parcel, a fictional delivery service. Replace its domains,\nrepository names, and resource identifiers with your own. The hosted\npetstore Spec is a runnable sample.\n";
|
|
56
56
|
const DOCS_URL_DEFAULT: string | null = "https://typeship.dev";
|
|
57
57
|
const DOCS_INDEX_URL_DEFAULT: string | null = null;
|
|
58
58
|
const RELAY: { mintUrl: string; project: string } | null = null;
|
|
@@ -602,7 +602,7 @@ async function startOAuthBrowserSession(parsed: Parsed, clientId: string, timeou
|
|
|
602
602
|
audience: OAUTH_TOKEN_PARAMS.audience, resource: OAUTH_TOKEN_PARAMS.resource,
|
|
603
603
|
organization: requestedLoginOrganization(parsed.flags),
|
|
604
604
|
}, { signal: controller.signal, timeoutMs, authorize(url) {
|
|
605
|
-
process.stderr.write("Sign in
|
|
605
|
+
process.stderr.write("Sign in with your existing credentials: " + url + "\n");
|
|
606
606
|
if (isAgentMode(parsed) || parsed.flags.get("no-browser") === true) process.stderr.write(JSON.stringify({ event: "oauth_browser", authorization_url: url, note: "Open this URL in a browser on the same computer as the CLI." }) + "\n");
|
|
607
607
|
else openInBrowser(url);
|
|
608
608
|
} });
|
|
@@ -780,7 +780,7 @@ async function cmdWhoami(parsed: Parsed): Promise<void> {
|
|
|
780
780
|
if (op) {
|
|
781
781
|
const client = await makeClient(parsed.flags, op);
|
|
782
782
|
const target = (client as unknown as Record<string, Record<string, () => Promise<{ ok: boolean; data?: unknown; error?: unknown }>>>)[op.resource]!;
|
|
783
|
-
const result = await target[op.method]!();
|
|
783
|
+
const result = await asApiResult(target[op.method]!());
|
|
784
784
|
if (result.ok) { out(result.data ?? { ok: true }); await flushExit(0); }
|
|
785
785
|
failApi(result.error, LAST_CLIENT_HAD_CREDENTIAL);
|
|
786
786
|
}
|
|
@@ -1174,7 +1174,7 @@ async function cmdAuth(parsed: Parsed): Promise<void> {
|
|
|
1174
1174
|
if (op) {
|
|
1175
1175
|
const client = await makeClient(parsed.flags, op);
|
|
1176
1176
|
const target = (client as unknown as Record<string, Record<string, () => Promise<{ ok: boolean; data?: unknown; error?: unknown }>>>)[op.resource]!;
|
|
1177
|
-
const result = await target[op.method]!();
|
|
1177
|
+
const result = await asApiResult(target[op.method]!());
|
|
1178
1178
|
if (result.ok) {
|
|
1179
1179
|
if (identityConfiguration() && savedIdentity?.identity) assertApiIdentity(savedIdentity.identity.values, readApiIdentity(result.data, IDENTITY_POLICY));
|
|
1180
1180
|
report.identity = result.data;
|
|
@@ -1222,7 +1222,7 @@ async function cmdDoctor(parsed: Parsed): Promise<void> {
|
|
|
1222
1222
|
try {
|
|
1223
1223
|
const client = await makeClient(parsed.flags, op);
|
|
1224
1224
|
const target = (client as unknown as Record<string, Record<string, () => Promise<{ ok: boolean; error?: unknown }>>>)[op.resource]!;
|
|
1225
|
-
const result = await target[op.method]!();
|
|
1225
|
+
const result = await asApiResult(target[op.method]!());
|
|
1226
1226
|
checks.push(result.ok ? { name: "identity", ok: true, detail: op.command.join(" ") + " ok" } : { name: "identity", ok: false, detail: classifyApiError(result.error, { bin: BIN, hadCredential: true, docsUrl: DOCS_URL_DEFAULT }).message, fix: "The credential was rejected; run '" + BIN + " login' with a current one." });
|
|
1227
1227
|
} catch (e) {
|
|
1228
1228
|
checks.push({ name: "identity", ok: false, detail: (e as Error).message });
|
|
@@ -1957,7 +1957,7 @@ function printRoot(stream: NodeJS.WriteStream = process.stdout): void {
|
|
|
1957
1957
|
}
|
|
1958
1958
|
const width = termWidth();
|
|
1959
1959
|
const lines: string[] = [];
|
|
1960
|
-
lines.push(paintOut("bold", BIN) + ": " + "typeship API" + " (v" + "1.0.0" + "), package " + "0.
|
|
1960
|
+
lines.push(paintOut("bold", BIN) + ": " + "typeship API" + " (v" + "1.0.0" + "), package " + "0.21.0");
|
|
1961
1961
|
lines.push("");
|
|
1962
1962
|
lines.push(paintOut("bold", "Usage:") + " " + BIN + " <resource> <command> [args] [--flags]");
|
|
1963
1963
|
lines.push("");
|
|
@@ -1989,7 +1989,7 @@ function printRoot(stream: NodeJS.WriteStream = process.stdout): void {
|
|
|
1989
1989
|
...(BASIC ? [BASIC.envUser, BASIC.envPass] : []),
|
|
1990
1990
|
].join(", ") || "none", width, 21));
|
|
1991
1991
|
lines.push(...labeled("Endpoint env var: ", "TYPESHIP_BASE_URL", width, 18));
|
|
1992
|
-
lines.push(...labeled("
|
|
1992
|
+
lines.push(...labeled("Sign-in: ", BIN + " login | logout | whoami | auth check (stored at " + credsPath() + ")", width, 9));
|
|
1993
1993
|
lines.push(...labeled("Setup: ", BIN + " init (connect this machine)" + " | " + BIN + " config (defaults)" + (HAS_MCP || MCP_URL ? " | " + BIN + " mcp install --all (agent clients)" : "") + " | " + BIN + " doctor | " + BIN + " upgrade | " + BIN + " completion <shell>", width, 7));
|
|
1994
1994
|
lines.push(...labeled("Agents: ", BIN + " agent-guide | " + BIN + " help --json | --mode agent | -y/--yes/--force | --out <dir> (JSON errors: {status, issues[{code}], next_steps})", width, 8));
|
|
1995
1995
|
lines.push(...labeled("Docs: ", BIN + " docs [<resource> <command> | search <term> | read <page> | --web]", width, 6));
|
|
@@ -2277,7 +2277,7 @@ function requireOperationCredentials(op: OpSpec, options: ClientOptions & Record
|
|
|
2277
2277
|
...relevant.map((a) => "Set " + a.env + " in the environment, pass --" + a.flag + " <value>, or run '" + BIN + " login'."),
|
|
2278
2278
|
...(needsBasic && BASIC ? ["Set " + BASIC.envUser + " and " + BASIC.envPass + ", or pass --username and --password."] : []),
|
|
2279
2279
|
"Supply all schemes in one alternative through " + "TYPESHIP_CREDENTIALS" + " or --credentials @<JSON-file>: " + alternatives.map((alternative) => alternative.map((option) => option.slice("credentials.".length)).join(" + ")).join(" OR ") + ".",
|
|
2280
|
-
] : ["Check the operation's security schemes in the API
|
|
2280
|
+
] : ["Check the operation's security schemes in the API Spec and regenerate with a supported, compatible alternative."],
|
|
2281
2281
|
});
|
|
2282
2282
|
}
|
|
2283
2283
|
|
|
@@ -2438,7 +2438,7 @@ function failOmitted(op: OmittedOpSpec): never {
|
|
|
2438
2438
|
return failWith({
|
|
2439
2439
|
status: "action_required",
|
|
2440
2440
|
code: "PLAN_LIMIT",
|
|
2441
|
-
message: "The command '" + BIN + " " + op.command.join(" ") + "' exists in the API
|
|
2441
|
+
message: "The command '" + BIN + " " + op.command.join(" ") + "' exists in the API Spec but was omitted from this generated package by its plan limit.",
|
|
2442
2442
|
detail: { operation: op.tool, method: op.httpMethod, path: op.path, generated_operations: OPS.length, total_operations: OPS.length + EXCLUDED_OPS },
|
|
2443
2443
|
nextSteps: ["Upgrade at https://typeship.dev/pricing and regenerate the package without the operation cap.", "Do not invent or retry an omitted command against this generated package."],
|
|
2444
2444
|
});
|
|
@@ -2647,13 +2647,13 @@ async function main(): Promise<void> {
|
|
|
2647
2647
|
}
|
|
2648
2648
|
}
|
|
2649
2649
|
|
|
2650
|
-
let result = await (callResult as Promise<
|
|
2651
|
-
if (result.ok && op.httpMethod === "POST" && op.path === "/projects/{project_id}/
|
|
2650
|
+
let result = await asApiResult(callResult as Promise<unknown>);
|
|
2651
|
+
if (result.ok && op.httpMethod === "POST" && op.path === "/projects/{project_id}/generate") {
|
|
2652
2652
|
const batch = result.data as { data: Array<{ id: string }> };
|
|
2653
|
-
const generations = (client as unknown as { generations: { wait(id: string): Promise<
|
|
2653
|
+
const generations = (client as unknown as { generations: { wait(id: string): Promise<unknown> } }).generations;
|
|
2654
2654
|
const completed: unknown[] = [];
|
|
2655
2655
|
for (const generation of batch.data) {
|
|
2656
|
-
const waited = await generations.wait(generation.id);
|
|
2656
|
+
const waited = await asApiResult(generations.wait(generation.id));
|
|
2657
2657
|
if (!waited.ok) failApi(waited.error, LAST_CLIENT_HAD_CREDENTIAL);
|
|
2658
2658
|
completed.push(waited.data);
|
|
2659
2659
|
}
|
package/src/core/http.ts
CHANGED
|
@@ -19,7 +19,7 @@ export interface ResponseMeta {
|
|
|
19
19
|
headers: Headers;
|
|
20
20
|
/** Parsed wire body before it is narrowed to the generated response type.
|
|
21
21
|
* Use this escape hatch for additive fields or variants introduced after
|
|
22
|
-
* this generator
|
|
22
|
+
* this generator version. */
|
|
23
23
|
rawBody?: unknown;
|
|
24
24
|
/** Request identifier from the JSON response body, or from headers for
|
|
25
25
|
* raw and bodyless responses. */
|
|
@@ -43,11 +43,7 @@ export interface RequestContext {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
|
|
46
|
-
/**
|
|
47
|
-
* Every SDK call returns a discriminated result instead of throwing.
|
|
48
|
-
* Narrow on `ok` and the error side is a typed union of the documented
|
|
49
|
-
* error responses for that exact operation.
|
|
50
|
-
*/
|
|
46
|
+
/** Internal result used by CLI and MCP consumers of the shared core. */
|
|
51
47
|
export type ApiResult<T, E> =
|
|
52
48
|
| { ok: true; data: T; response: ResponseMeta }
|
|
53
49
|
| { ok: false; error: E; response?: ResponseMeta };
|
|
@@ -59,15 +55,80 @@ export function unwrap<T, E>(result: ApiResult<T, E>): T {
|
|
|
59
55
|
throw new Error(String(result.error));
|
|
60
56
|
}
|
|
61
57
|
|
|
58
|
+
/** Adapter for generated CLI and MCP entry points that consume result objects. */
|
|
59
|
+
export async function asApiResult<T>(call: PromiseLike<T>): Promise<{ ok: boolean; data?: T; error?: unknown; response?: ResponseMeta }> {
|
|
60
|
+
try {
|
|
61
|
+
const data = await call;
|
|
62
|
+
const pageResponse = data && typeof data === "object" && "response" in data
|
|
63
|
+
? (data as { response?: ResponseMeta }).response : undefined;
|
|
64
|
+
const response = pageResponse ?? {
|
|
65
|
+
status: 0,
|
|
66
|
+
headers: new Headers(),
|
|
67
|
+
rawBody: data,
|
|
68
|
+
requestId: requestIdFromBody(data),
|
|
69
|
+
};
|
|
70
|
+
return { ok: true, data, response };
|
|
71
|
+
} catch (error) {
|
|
72
|
+
const response = error && typeof error === "object" && "response" in error
|
|
73
|
+
? (error as { response?: ResponseMeta }).response : undefined;
|
|
74
|
+
return { ok: false, error, response };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Common fields on every failure. A missing response has status null. */
|
|
79
|
+
export class SdkError extends Error {
|
|
80
|
+
readonly code: string;
|
|
81
|
+
readonly status: number | null;
|
|
82
|
+
readonly requestId?: string;
|
|
83
|
+
readonly body: unknown;
|
|
84
|
+
|
|
85
|
+
constructor(message: string, code: string, status: number | null = null, body?: unknown, requestId?: string) {
|
|
86
|
+
super(message);
|
|
87
|
+
this.name = new.target.name;
|
|
88
|
+
this.code = code;
|
|
89
|
+
this.status = status;
|
|
90
|
+
this.body = body;
|
|
91
|
+
this.requestId = requestId;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function errorCode(body: unknown, fallback: string): string {
|
|
96
|
+
if (body && typeof body === "object" && !Array.isArray(body)) {
|
|
97
|
+
const value = body as Record<string, unknown>;
|
|
98
|
+
const first = Array.isArray(value.errors) ? value.errors[0] as { code?: unknown } | undefined : undefined;
|
|
99
|
+
const code = value.code ?? first?.code;
|
|
100
|
+
if (typeof code === "string" && code.trim()) return code;
|
|
101
|
+
}
|
|
102
|
+
return fallback;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function errorDetail(body: unknown): string {
|
|
106
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) return "";
|
|
107
|
+
const value = body as Record<string, unknown>;
|
|
108
|
+
const first = Array.isArray(value.errors) ? value.errors[0] as { message?: unknown } | undefined : undefined;
|
|
109
|
+
const detail = value.message ?? value.error ?? value.detail ?? first?.message;
|
|
110
|
+
return typeof detail === "string" && detail.trim() ? ": " + detail : "";
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function nextStep(status: number): string {
|
|
114
|
+
if (status === 401) return "Check the credential and retry.";
|
|
115
|
+
if (status === 403) return "Check the credential's permissions and retry.";
|
|
116
|
+
if (status === 404) return "Check the requested identifier or path.";
|
|
117
|
+
if (status === 409) return "Refresh the resource and retry the change.";
|
|
118
|
+
if (status === 422 || status === 400) return "Correct the request and retry.";
|
|
119
|
+
if (status === 429) return "Wait before retrying the request.";
|
|
120
|
+
if (status >= 500) return "Retry later; contact the API provider if this continues.";
|
|
121
|
+
return "Inspect the error body and correct the request before retrying.";
|
|
122
|
+
}
|
|
123
|
+
|
|
62
124
|
/** Base class for every HTTP error response. */
|
|
63
|
-
export class ApiError<S extends number = number, B = unknown> extends
|
|
125
|
+
export class ApiError<S extends number = number, B = unknown> extends SdkError {
|
|
64
126
|
readonly status: S;
|
|
65
127
|
readonly body: B;
|
|
66
128
|
readonly response: ResponseMeta;
|
|
67
129
|
|
|
68
130
|
constructor(message: string, status: S, body: B, response: ResponseMeta) {
|
|
69
|
-
super(message);
|
|
70
|
-
this.name = new.target.name;
|
|
131
|
+
super(message + errorDetail(body) + ". " + nextStep(status), errorCode(body, "http_" + status), status, body, response.requestId);
|
|
71
132
|
this.status = status;
|
|
72
133
|
this.body = body;
|
|
73
134
|
this.response = response;
|
|
@@ -82,15 +143,14 @@ export class UnexpectedApiError extends ApiError<number, unknown> {
|
|
|
82
143
|
}
|
|
83
144
|
|
|
84
145
|
/** A successful response declared JSON but carried a body that could not be parsed. */
|
|
85
|
-
export class ResponseParseError extends
|
|
146
|
+
export class ResponseParseError extends SdkError {
|
|
86
147
|
readonly status: number;
|
|
87
148
|
readonly body: string;
|
|
88
149
|
readonly response: ResponseMeta;
|
|
89
150
|
override readonly cause?: unknown;
|
|
90
151
|
|
|
91
152
|
constructor(body: string, response: ResponseMeta, cause?: unknown) {
|
|
92
|
-
super("HTTP " + response.status + " response body was not valid JSON");
|
|
93
|
-
this.name = "ResponseParseError";
|
|
153
|
+
super("HTTP " + response.status + " response body was not valid JSON. Check the API response or contact its provider.", "response_parse_error", response.status, body, response.requestId);
|
|
94
154
|
this.status = response.status;
|
|
95
155
|
this.body = body;
|
|
96
156
|
this.response = response;
|
|
@@ -105,18 +165,16 @@ export class ResponseParseError extends Error {
|
|
|
105
165
|
|
|
106
166
|
export interface Violation { path: string; message: string }
|
|
107
167
|
|
|
108
|
-
/** Request or response data did not match the spec's schema
|
|
109
|
-
|
|
110
|
-
* ApiResult, like every other failure. */
|
|
111
|
-
export class ValidationError extends Error {
|
|
168
|
+
/** Request or response data did not match the spec's schema. */
|
|
169
|
+
export class ValidationError extends SdkError {
|
|
112
170
|
readonly direction: "request" | "response";
|
|
113
171
|
readonly target: "body" | "parameters";
|
|
114
172
|
readonly violations: Violation[];
|
|
115
173
|
constructor(direction: "request" | "response", violations: Violation[], target: "body" | "parameters" = "body") {
|
|
116
174
|
const shown = violations.slice(0, 3).map((v) => v.path + " " + v.message).join("; ");
|
|
117
175
|
super(direction + " " + target + " failed schema validation: " + shown
|
|
118
|
-
+ (violations.length > 3 ? " (+" + (violations.length - 3) + " more)" : "")
|
|
119
|
-
|
|
176
|
+
+ (violations.length > 3 ? " (+" + (violations.length - 3) + " more)" : "")
|
|
177
|
+
+ ". Correct the " + (direction === "request" ? "request" : "API response or update the spec") + ".", "validation_error");
|
|
120
178
|
this.direction = direction;
|
|
121
179
|
this.target = target;
|
|
122
180
|
this.violations = violations;
|
|
@@ -251,13 +309,14 @@ function transportFailureMessage(method: string, url: string, cause: unknown): s
|
|
|
251
309
|
}
|
|
252
310
|
|
|
253
311
|
/** The request failed before a complete HTTP response arrived (network failure, timeout, abort, or truncated body). */
|
|
254
|
-
export class TransportError extends
|
|
312
|
+
export class TransportError extends SdkError {
|
|
255
313
|
override readonly cause?: unknown;
|
|
314
|
+
readonly response?: ResponseMeta;
|
|
256
315
|
|
|
257
|
-
constructor(message: string, cause?: unknown) {
|
|
258
|
-
super(message);
|
|
259
|
-
this.name = "TransportError";
|
|
316
|
+
constructor(message: string, cause?: unknown, response?: ResponseMeta) {
|
|
317
|
+
super(message + ". Check the connection and retry.", "transport_error", response?.status ?? null, undefined, response?.requestId);
|
|
260
318
|
this.cause = cause;
|
|
319
|
+
this.response = response;
|
|
261
320
|
}
|
|
262
321
|
}
|
|
263
322
|
|
|
@@ -521,6 +580,7 @@ export class HttpCore {
|
|
|
521
580
|
const error = (parseError ?? new TransportError(
|
|
522
581
|
"The response body read failed before completing",
|
|
523
582
|
cause,
|
|
583
|
+
meta(response),
|
|
524
584
|
)) as unknown as E;
|
|
525
585
|
await this.config.onError?.(error, { method: req.method, path: req.path });
|
|
526
586
|
return { ok: false, error, response: parseError?.response ?? meta(response) };
|
|
@@ -567,8 +627,8 @@ export class HttpCore {
|
|
|
567
627
|
let body: unknown;
|
|
568
628
|
try {
|
|
569
629
|
body = await parseBody(response, req.method);
|
|
570
|
-
} catch {
|
|
571
|
-
body =
|
|
630
|
+
} catch (cause) {
|
|
631
|
+
body = cause instanceof ResponseParseError ? cause.body : undefined;
|
|
572
632
|
}
|
|
573
633
|
emitResponseDebug(response, body);
|
|
574
634
|
const responseMeta = meta(response, body);
|
|
@@ -589,6 +649,11 @@ export class HttpCore {
|
|
|
589
649
|
return { ok: false, error };
|
|
590
650
|
}
|
|
591
651
|
|
|
652
|
+
/** SDK-facing call: resolve to the payload or throw its typed error. */
|
|
653
|
+
async requestData<T, E>(req: CoreRequest): Promise<T> {
|
|
654
|
+
return unwrap(await this.request<T, E>(req));
|
|
655
|
+
}
|
|
656
|
+
|
|
592
657
|
private async send(
|
|
593
658
|
req: CoreRequest,
|
|
594
659
|
timeoutMs: number,
|
package/src/core/pagination.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auto-pagination. Generated by typeship — https://typeship.dev
|
|
3
3
|
*
|
|
4
|
-
* List operations return a PagePromise: `await` it for one page
|
|
5
|
-
*
|
|
4
|
+
* List operations return a PagePromise: `await` it for one page, or
|
|
5
|
+
* `for await` it to stream every item across every page.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import {
|
|
9
9
|
HttpCore,
|
|
10
|
-
type ApiResult,
|
|
11
10
|
type CoreRequest,
|
|
12
11
|
type ResponseMeta,
|
|
13
12
|
} from "./http.js";
|
|
@@ -29,7 +28,7 @@ export interface PageConfig {
|
|
|
29
28
|
|
|
30
29
|
type FetchPage<Item, E> = (
|
|
31
30
|
query: Record<string, unknown>,
|
|
32
|
-
) => Promise<
|
|
31
|
+
) => Promise<Page<Item, E>>;
|
|
33
32
|
|
|
34
33
|
export class Page<Item, E = unknown> {
|
|
35
34
|
private readonly fetchPage: FetchPage<Item, E>;
|
|
@@ -85,11 +84,7 @@ export class Page<Item, E = unknown> {
|
|
|
85
84
|
async getNextPage(): Promise<Page<Item, E> | null> {
|
|
86
85
|
const next = this.nextPageParams();
|
|
87
86
|
if (next === null) return null;
|
|
88
|
-
|
|
89
|
-
if (!result.ok) {
|
|
90
|
-
throw result.error instanceof Error ? result.error : new Error(String(result.error));
|
|
91
|
-
}
|
|
92
|
-
return result.data;
|
|
87
|
+
return this.fetchPage(next);
|
|
93
88
|
}
|
|
94
89
|
|
|
95
90
|
/** Iterate every item on this page and all following pages. */
|
|
@@ -114,28 +109,24 @@ export class Page<Item, E = unknown> {
|
|
|
114
109
|
}
|
|
115
110
|
|
|
116
111
|
/**
|
|
117
|
-
* Awaitable (one page
|
|
112
|
+
* Awaitable (one page) and async-iterable (all items,
|
|
118
113
|
* all pages). Iteration throws the operation's typed error union on failure.
|
|
119
114
|
*/
|
|
120
|
-
export class PagePromise<Item, E> implements PromiseLike<
|
|
121
|
-
private readonly first: Promise<
|
|
122
|
-
constructor(first: Promise<
|
|
115
|
+
export class PagePromise<Item, E> implements PromiseLike<Page<Item, E>>, AsyncIterable<Item> {
|
|
116
|
+
private readonly first: Promise<Page<Item, E>>;
|
|
117
|
+
constructor(first: Promise<Page<Item, E>>) {
|
|
123
118
|
this.first = first;
|
|
124
119
|
}
|
|
125
120
|
|
|
126
|
-
then<R1 =
|
|
127
|
-
onfulfilled?: ((value:
|
|
121
|
+
then<R1 = Page<Item, E>, R2 = never>(
|
|
122
|
+
onfulfilled?: ((value: Page<Item, E>) => R1 | PromiseLike<R1>) | null,
|
|
128
123
|
onrejected?: ((reason: unknown) => R2 | PromiseLike<R2>) | null,
|
|
129
124
|
): Promise<R1 | R2> {
|
|
130
125
|
return this.first.then(onfulfilled, onrejected);
|
|
131
126
|
}
|
|
132
127
|
|
|
133
128
|
async *[Symbol.asyncIterator](): AsyncIterator<Item, void, undefined> {
|
|
134
|
-
|
|
135
|
-
if (!result.ok) {
|
|
136
|
-
throw result.error instanceof Error ? result.error : new Error(String(result.error));
|
|
137
|
-
}
|
|
138
|
-
yield* result.data;
|
|
129
|
+
yield* await this.first;
|
|
139
130
|
}
|
|
140
131
|
}
|
|
141
132
|
|
|
@@ -149,9 +140,8 @@ export function paginate<Item, E>(
|
|
|
149
140
|
const fetchPage: FetchPage<Item, E> = async (params) => {
|
|
150
141
|
let nextReq: CoreRequest = { ...req, query: params };
|
|
151
142
|
const result = await core.request<unknown, E>(nextReq);
|
|
152
|
-
if (!result.ok)
|
|
153
|
-
|
|
154
|
-
return { ok: true, data: page, response: result.response };
|
|
143
|
+
if (!result.ok) throw result.error instanceof Error ? result.error : new Error(String(result.error));
|
|
144
|
+
return new Page<Item, E>(fetchPage, config, params, result.data, result.response);
|
|
155
145
|
};
|
|
156
146
|
const initial: Record<string, unknown> = {};
|
|
157
147
|
let seed = req.query ?? {};
|
package/src/errors.ts
CHANGED
|
@@ -4,15 +4,15 @@
|
|
|
4
4
|
import { ApiError, type ResponseMeta } from "./core/http.js";
|
|
5
5
|
import type { ErrorModel, ErrorModelRead } from "./types.js";
|
|
6
6
|
|
|
7
|
-
export { ApiError, ResponseParseError, TransportError, UnexpectedApiError, ValidationError, type Violation
|
|
7
|
+
export { ApiError, SdkError, ResponseParseError, TransportError, UnexpectedApiError, ValidationError, type Violation } from "./core/http.js";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* The request body,
|
|
10
|
+
* The request body, Spec source, target selection, or package name is invalid.
|
|
11
11
|
* Raised for HTTP 400 responses.
|
|
12
12
|
*/
|
|
13
13
|
export class BadRequestError extends ApiError<400, ErrorModelRead> {
|
|
14
14
|
constructor(body: ErrorModelRead, response: ResponseMeta) {
|
|
15
|
-
super("The request body,
|
|
15
|
+
super("The request body, Spec source, target selection, or package name is invalid.", 400, body, response);
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -57,12 +57,12 @@ export class PayloadTooLargeError extends ApiError<413, ErrorModelRead> {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
/**
|
|
60
|
-
* The
|
|
60
|
+
* The Spec could not be resolved or understood.
|
|
61
61
|
* Raised for HTTP 422 responses.
|
|
62
62
|
*/
|
|
63
63
|
export class UnprocessableEntityError extends ApiError<422, ErrorModelRead> {
|
|
64
64
|
constructor(body: ErrorModelRead, response: ResponseMeta) {
|
|
65
|
-
super("The
|
|
65
|
+
super("The Spec could not be resolved or understood.", 422, body, response);
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
|
|
@@ -98,12 +98,12 @@ export class ApiResponseError extends ApiError<number, ErrorModelRead> {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
/**
|
|
101
|
-
* No such resource in this
|
|
101
|
+
* No such resource in this organization.
|
|
102
102
|
* Raised for HTTP 404 responses.
|
|
103
103
|
*/
|
|
104
104
|
export class NotFoundError extends ApiError<404, ErrorModelRead> {
|
|
105
105
|
constructor(body: ErrorModelRead, response: ResponseMeta) {
|
|
106
|
-
super("No such resource in this
|
|
106
|
+
super("No such resource in this organization.", 404, body, response);
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
|
package/src/index.ts
CHANGED
|
@@ -14,16 +14,21 @@ import { DEFS, SCHEMAS } from "./schemas.js";
|
|
|
14
14
|
|
|
15
15
|
import { GenerateResource } from "./resources/generate.js";
|
|
16
16
|
import { ProjectsResource } from "./resources/projects.js";
|
|
17
|
-
import {
|
|
17
|
+
import { SpecsResource } from "./resources/specs.js";
|
|
18
|
+
import { SpecRevisionsResource } from "./resources/spec-revisions.js";
|
|
18
19
|
import { TargetsResource } from "./resources/targets.js";
|
|
20
|
+
import { DraftsResource } from "./resources/drafts.js";
|
|
21
|
+
import { ReleasesResource } from "./resources/releases.js";
|
|
22
|
+
import { DeliveriesResource } from "./resources/deliveries.js";
|
|
23
|
+
import { PublicationsResource } from "./resources/publications.js";
|
|
19
24
|
import { GenerationsResource } from "./resources/generations.js";
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
25
|
+
import { FilesResource } from "./resources/files.js";
|
|
26
|
+
import { OrganizationResource } from "./resources/organization.js";
|
|
22
27
|
import { ApiKeysResource } from "./resources/api-keys.js";
|
|
23
28
|
|
|
24
29
|
/** This package's version, also sent as the `User-Agent`. */
|
|
25
|
-
export const VERSION = "0.
|
|
26
|
-
const USER_AGENT = "@typeship-ax/cli/0.
|
|
30
|
+
export const VERSION = "0.21.0";
|
|
31
|
+
const USER_AGENT = "@typeship-ax/cli/0.21.0";
|
|
27
32
|
|
|
28
33
|
export interface ClientOptions {
|
|
29
34
|
/** Override the server URL. Default: `https://typeship.dev/api/v1` */
|
|
@@ -34,8 +39,8 @@ export interface ClientOptions {
|
|
|
34
39
|
*/
|
|
35
40
|
bearerToken?: string | (() => string | Promise<string>);
|
|
36
41
|
/**
|
|
37
|
-
* Credentials keyed by the
|
|
38
|
-
*
|
|
42
|
+
* Credentials keyed by the Spec's security scheme names. Only one complete alternative is sent
|
|
43
|
+
* for each operation. Named values take precedence over convenience options.
|
|
39
44
|
*/
|
|
40
45
|
credentials?: {
|
|
41
46
|
"apiKey"?: AuthValue;
|
|
@@ -71,17 +76,16 @@ export interface ClientOptions {
|
|
|
71
76
|
debug?: boolean | ((event: DebugEvent) => void);
|
|
72
77
|
/**
|
|
73
78
|
* Opt-in zero-dependency runtime validation of JSON bodies against the spec's schemas: true
|
|
74
|
-
* checks requests and responses and
|
|
75
|
-
*
|
|
76
|
-
* drift the type system can't see.
|
|
79
|
+
* checks requests and responses and throws ValidationError on mismatch; mode "warn" logs via
|
|
80
|
+
* console.warn and lets the call proceed. Catches spec drift the type system can't see.
|
|
77
81
|
*/
|
|
78
82
|
validate?: boolean | { requests?: boolean; responses?: boolean; mode?: "throw" | "warn" };
|
|
79
83
|
}
|
|
80
84
|
|
|
81
85
|
/**
|
|
82
|
-
* typeship — v0.
|
|
86
|
+
* typeship — v0.21.0
|
|
83
87
|
*
|
|
84
|
-
* Resolve an OpenAPI or GraphQL
|
|
88
|
+
* Resolve an OpenAPI or GraphQL Spec, diagnose it, and keep every
|
|
85
89
|
* selected CLI, MCP, and SDK Target current.
|
|
86
90
|
*
|
|
87
91
|
* Every operation but one requires a bearer credential: an organization
|
|
@@ -93,16 +97,21 @@ export interface ClientOptions {
|
|
|
93
97
|
*
|
|
94
98
|
* Examples use Parcel, a fictional delivery service. Replace its domains,
|
|
95
99
|
* repository names, and resource identifiers with your own. The hosted
|
|
96
|
-
* petstore
|
|
100
|
+
* petstore Spec is a runnable sample.
|
|
97
101
|
*/
|
|
98
102
|
export class TypeshipClient {
|
|
99
103
|
readonly generate: GenerateResource;
|
|
100
104
|
readonly projects: ProjectsResource;
|
|
101
|
-
readonly
|
|
105
|
+
readonly specs: SpecsResource;
|
|
106
|
+
readonly specRevisions: SpecRevisionsResource;
|
|
102
107
|
readonly targets: TargetsResource;
|
|
108
|
+
readonly drafts: DraftsResource;
|
|
109
|
+
readonly releases: ReleasesResource;
|
|
110
|
+
readonly deliveries: DeliveriesResource;
|
|
111
|
+
readonly publications: PublicationsResource;
|
|
103
112
|
readonly generations: GenerationsResource;
|
|
104
|
-
readonly
|
|
105
|
-
readonly
|
|
113
|
+
readonly files: FilesResource;
|
|
114
|
+
readonly organization: OrganizationResource;
|
|
106
115
|
readonly apiKeys: ApiKeysResource;
|
|
107
116
|
|
|
108
117
|
constructor(options: ClientOptions = {}) {
|
|
@@ -160,11 +169,16 @@ export class TypeshipClient {
|
|
|
160
169
|
});
|
|
161
170
|
this.generate = new GenerateResource(core);
|
|
162
171
|
this.projects = new ProjectsResource(core);
|
|
163
|
-
this.
|
|
172
|
+
this.specs = new SpecsResource(core);
|
|
173
|
+
this.specRevisions = new SpecRevisionsResource(core);
|
|
164
174
|
this.targets = new TargetsResource(core);
|
|
175
|
+
this.drafts = new DraftsResource(core);
|
|
176
|
+
this.releases = new ReleasesResource(core);
|
|
177
|
+
this.deliveries = new DeliveriesResource(core);
|
|
178
|
+
this.publications = new PublicationsResource(core);
|
|
165
179
|
this.generations = new GenerationsResource(core);
|
|
166
|
-
this.
|
|
167
|
-
this.
|
|
180
|
+
this.files = new FilesResource(core);
|
|
181
|
+
this.organization = new OrganizationResource(core);
|
|
168
182
|
this.apiKeys = new ApiKeysResource(core);
|
|
169
183
|
}
|
|
170
184
|
}
|
|
@@ -173,7 +187,6 @@ export * from "./types.js";
|
|
|
173
187
|
export * from "./errors.js";
|
|
174
188
|
export {
|
|
175
189
|
formatDebugEvent,
|
|
176
|
-
type ApiResult,
|
|
177
190
|
type AuthValue,
|
|
178
191
|
type DebugEvent,
|
|
179
192
|
type RequestContext,
|
|
@@ -184,9 +197,14 @@ export { Page, PagePromise } from "./core/pagination.js";
|
|
|
184
197
|
|
|
185
198
|
export * from "./resources/generate.js";
|
|
186
199
|
export * from "./resources/projects.js";
|
|
187
|
-
export * from "./resources/
|
|
200
|
+
export * from "./resources/specs.js";
|
|
201
|
+
export * from "./resources/spec-revisions.js";
|
|
188
202
|
export * from "./resources/targets.js";
|
|
203
|
+
export * from "./resources/drafts.js";
|
|
204
|
+
export * from "./resources/releases.js";
|
|
205
|
+
export * from "./resources/deliveries.js";
|
|
206
|
+
export * from "./resources/publications.js";
|
|
189
207
|
export * from "./resources/generations.js";
|
|
190
|
-
export * from "./resources/
|
|
191
|
-
export * from "./resources/
|
|
208
|
+
export * from "./resources/files.js";
|
|
209
|
+
export * from "./resources/organization.js";
|
|
192
210
|
export * from "./resources/api-keys.js";
|