@typeship-ax/cli 0.22.0 → 0.23.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/AGENTS.md +12 -8
- package/README.md +14 -27
- package/api.json +8898 -9026
- package/api.md +369 -327
- package/dist/arguments.d.ts +47 -0
- package/dist/arguments.d.ts.map +1 -0
- package/dist/arguments.js +254 -0
- package/dist/cli-agent.d.ts +31 -8
- package/dist/cli-agent.d.ts.map +1 -1
- package/dist/cli-agent.js +146 -28
- package/dist/cli.js +483 -237
- package/dist/core/http.d.ts +162 -19
- package/dist/core/http.d.ts.map +1 -1
- package/dist/core/http.js +381 -48
- package/dist/core/pagination.d.ts +42 -6
- package/dist/core/pagination.d.ts.map +1 -1
- package/dist/core/pagination.js +111 -17
- package/dist/credential-storage.d.ts +10 -3
- package/dist/credential-storage.d.ts.map +1 -1
- package/dist/credential-storage.js +15 -6
- package/dist/dates.d.ts +1 -1
- package/dist/dates.js +1 -1
- package/dist/errors.d.ts +20 -84
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +20 -108
- package/dist/fields.d.ts +29 -0
- package/dist/fields.d.ts.map +1 -0
- package/dist/fields.js +101 -0
- package/dist/index.d.ts +28 -18
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -25
- package/dist/named-credentials.d.ts +19 -0
- package/dist/named-credentials.d.ts.map +1 -1
- package/dist/named-credentials.js +81 -1
- package/dist/oauth-login.d.ts +8 -2
- package/dist/oauth-login.d.ts.map +1 -1
- package/dist/oauth-login.js +31 -19
- package/dist/oauth-request.d.ts +7 -1
- package/dist/oauth-request.d.ts.map +1 -1
- package/dist/oauth-request.js +26 -4
- package/dist/oauth-session.d.ts +13 -1
- package/dist/oauth-session.d.ts.map +1 -1
- package/dist/oauth-session.js +34 -18
- package/dist/ops.d.ts +53 -5
- package/dist/ops.d.ts.map +1 -1
- package/dist/ops.js +49 -40
- package/dist/polling-login.d.ts +8 -2
- package/dist/polling-login.d.ts.map +1 -1
- package/dist/polling-login.js +25 -11
- package/dist/resources/api-keys.d.ts +10 -7
- package/dist/resources/api-keys.d.ts.map +1 -1
- package/dist/resources/api-keys.js +10 -31
- package/dist/resources/deliveries.d.ts +88 -4
- package/dist/resources/deliveries.d.ts.map +1 -1
- package/dist/resources/deliveries.js +95 -18
- package/dist/resources/drafts.d.ts +15 -15
- package/dist/resources/drafts.d.ts.map +1 -1
- package/dist/resources/drafts.js +11 -64
- package/dist/resources/files.d.ts +4 -4
- package/dist/resources/files.d.ts.map +1 -1
- package/dist/resources/files.js +3 -12
- package/dist/resources/generations.d.ts +14 -14
- package/dist/resources/generations.d.ts.map +1 -1
- package/dist/resources/generations.js +21 -45
- package/dist/resources/organization.d.ts +4 -4
- package/dist/resources/organization.d.ts.map +1 -1
- package/dist/resources/organization.js +3 -10
- package/dist/resources/{generate.d.ts → packages.d.ts} +16 -16
- package/dist/resources/packages.d.ts.map +1 -0
- package/dist/resources/{generate.js → packages.js} +13 -29
- package/dist/resources/projects.d.ts +50 -50
- package/dist/resources/projects.d.ts.map +1 -1
- package/dist/resources/projects.js +60 -116
- package/dist/resources/releases.d.ts +21 -16
- package/dist/resources/releases.d.ts.map +1 -1
- package/dist/resources/releases.js +18 -39
- package/dist/resources/spec-revisions.d.ts +15 -6
- package/dist/resources/spec-revisions.d.ts.map +1 -1
- package/dist/resources/spec-revisions.js +6 -28
- package/dist/resources/specs.d.ts +7 -7
- package/dist/resources/specs.d.ts.map +1 -1
- package/dist/resources/specs.js +6 -34
- package/dist/resources/targets.d.ts +48 -48
- package/dist/resources/targets.d.ts.map +1 -1
- package/dist/resources/targets.js +58 -114
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +83 -81
- package/dist/search.d.ts +54 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +421 -0
- package/dist/types.d.ts +499 -339
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +18 -18
- package/package.json +5 -2
- package/src/arguments.ts +242 -0
- package/src/cli-agent.ts +156 -30
- package/src/cli.ts +444 -211
- package/src/core/http.ts +457 -58
- package/src/core/pagination.ts +129 -18
- package/src/credential-storage.ts +16 -6
- package/src/dates.ts +1 -1
- package/src/errors.ts +46 -115
- package/src/fields.ts +91 -0
- package/src/index.ts +45 -28
- package/src/named-credentials.ts +66 -1
- package/src/oauth-login.ts +36 -21
- package/src/oauth-request.ts +32 -6
- package/src/oauth-session.ts +37 -19
- package/src/ops.ts +82 -44
- package/src/polling-login.ts +24 -11
- package/src/resources/api-keys.ts +34 -48
- package/src/resources/deliveries.ts +211 -30
- package/src/resources/drafts.ts +60 -107
- package/src/resources/files.ts +19 -20
- package/src/resources/generations.ts +57 -75
- package/src/resources/organization.ts +11 -16
- package/src/resources/{generate.ts → packages.ts} +43 -51
- package/src/resources/projects.ts +145 -200
- package/src/resources/releases.ts +48 -65
- package/src/resources/spec-revisions.ts +38 -47
- package/src/resources/specs.ts +39 -59
- package/src/resources/targets.ts +143 -193
- package/src/schemas.ts +83 -81
- package/src/search.ts +434 -0
- package/src/types.ts +538 -357
- package/dist/console-login-check.d.ts +0 -21
- package/dist/console-login-check.d.ts.map +0 -1
- package/dist/console-login-check.js +0 -107
- package/dist/console-login-contract.d.ts +0 -45
- package/dist/console-login-contract.d.ts.map +0 -1
- package/dist/console-login-contract.js +0 -40
- package/dist/resources/generate.d.ts.map +0 -1
- package/dist/resources/publications.d.ts +0 -47
- package/dist/resources/publications.d.ts.map +0 -1
- package/dist/resources/publications.js +0 -70
- package/src/console-login-check.ts +0 -88
- package/src/console-login-contract.ts +0 -65
- package/src/resources/publications.ts +0 -140
package/src/core/pagination.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Auto-pagination. Generated by
|
|
2
|
+
* Auto-pagination. Generated by Typeship — https://typeship.dev
|
|
3
3
|
*
|
|
4
4
|
* List operations return a PagePromise: `await` it for one page, or
|
|
5
5
|
* `for await` it to stream every item across every page.
|
|
@@ -7,13 +7,15 @@
|
|
|
7
7
|
|
|
8
8
|
import {
|
|
9
9
|
HttpCore,
|
|
10
|
+
SdkError,
|
|
10
11
|
type CoreRequest,
|
|
11
12
|
type ResponseMeta,
|
|
12
13
|
} from "./http.js";
|
|
13
14
|
|
|
14
15
|
export interface PageConfig {
|
|
15
|
-
style: "cursor" | "cursorFromLastId" | "page" | "offset";
|
|
16
|
-
/** Response field holding the item array ("data", "items", …)
|
|
16
|
+
style: "cursor" | "cursorFromLastId" | "page" | "offset" | "nextUrl" | "link";
|
|
17
|
+
/** Response field holding the item array ("data", "items", …); "" when
|
|
18
|
+
* the body is the array itself. */
|
|
17
19
|
itemsField: string;
|
|
18
20
|
cursorParam?: string;
|
|
19
21
|
/** Dot-path into the response body ("next_cursor", "meta.next_cursor"). */
|
|
@@ -24,16 +26,41 @@ export interface PageConfig {
|
|
|
24
26
|
pageParam?: string;
|
|
25
27
|
offsetParam?: string;
|
|
26
28
|
limitParam?: string;
|
|
29
|
+
/** nextUrl: dot-path of the next page's URL ("next_page_uri", "next"). */
|
|
30
|
+
nextUrlField?: string;
|
|
31
|
+
/** page: the first page number when it is not 1. */
|
|
32
|
+
firstPage?: number;
|
|
33
|
+
/** Dot-path of the total item count: iteration stops once it is reached. */
|
|
34
|
+
totalField?: string;
|
|
35
|
+
/** Dot-path of the total page count: page iteration stops at the last page. */
|
|
36
|
+
totalPagesField?: string;
|
|
37
|
+
/** Each item is this field of the listed element (a Relay edge's `node`). */
|
|
38
|
+
itemPath?: string;
|
|
39
|
+
/** Page size sent when the caller gives none (GraphQL `first`). */
|
|
40
|
+
defaultLimit?: number;
|
|
41
|
+
/** Relay backward paging, used when the caller passes `backward.limitParam` (`last`). */
|
|
42
|
+
backward?: { limitParam: string; cursorParam: string; cursorField: string; hasMoreField: string };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** A list response the pagination rules cannot read. */
|
|
46
|
+
export class PaginationError extends SdkError {
|
|
47
|
+
constructor(message: string, response?: ResponseMeta) {
|
|
48
|
+
super(message, "pagination_error", response?.status ?? null, response?.rawBody, response?.requestId);
|
|
49
|
+
}
|
|
27
50
|
}
|
|
28
51
|
|
|
29
52
|
type FetchPage<Item, E> = (
|
|
30
53
|
query: Record<string, unknown>,
|
|
54
|
+
url?: string,
|
|
55
|
+
before?: number,
|
|
31
56
|
) => Promise<Page<Item, E>>;
|
|
32
57
|
|
|
33
58
|
export class Page<Item, E = unknown> {
|
|
34
59
|
private readonly fetchPage: FetchPage<Item, E>;
|
|
35
60
|
private readonly config: PageConfig;
|
|
36
61
|
private readonly params: Record<string, unknown>;
|
|
62
|
+
/** Items on the pages before this one, for the total-count stop. */
|
|
63
|
+
private readonly before: number;
|
|
37
64
|
readonly body: unknown;
|
|
38
65
|
readonly response: ResponseMeta;
|
|
39
66
|
|
|
@@ -43,48 +70,74 @@ export class Page<Item, E = unknown> {
|
|
|
43
70
|
params: Record<string, unknown>,
|
|
44
71
|
body: unknown,
|
|
45
72
|
response: ResponseMeta,
|
|
73
|
+
before = 0,
|
|
46
74
|
) {
|
|
47
75
|
this.fetchPage = fetchPage;
|
|
48
76
|
this.config = config;
|
|
49
77
|
this.params = params;
|
|
50
78
|
this.body = body;
|
|
51
79
|
this.response = response;
|
|
80
|
+
this.before = before;
|
|
52
81
|
}
|
|
53
82
|
|
|
54
83
|
get items(): Item[] {
|
|
55
|
-
const value = getPath(this.body, this.config.itemsField);
|
|
56
|
-
|
|
84
|
+
const value = this.config.itemsField === "" ? this.body : getPath(this.body, this.config.itemsField);
|
|
85
|
+
if (!Array.isArray(value)) return [];
|
|
86
|
+
const path = this.config.itemPath;
|
|
87
|
+
return path === undefined ? (value as Item[]) : value.map((entry) => (entry as Record<string, unknown> | null)?.[path] as Item);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** True when the caller asked for backward Relay pages (`last`). */
|
|
91
|
+
private get backward(): PageConfig["backward"] | undefined {
|
|
92
|
+
const backward = this.config.backward;
|
|
93
|
+
return backward && this.params[backward.limitParam] !== undefined ? backward : undefined;
|
|
57
94
|
}
|
|
58
95
|
|
|
59
96
|
hasNextPage(): boolean {
|
|
60
97
|
return this.nextPageParams() !== null;
|
|
61
98
|
}
|
|
62
99
|
|
|
100
|
+
/** The next page's URL, for styles that follow one (a response field or
|
|
101
|
+
* the Link header); null on the last page and for the other styles. */
|
|
102
|
+
nextPageUrl(): string | null {
|
|
103
|
+
if (this.finished()) return null;
|
|
104
|
+
switch (this.config.style) {
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
63
109
|
nextPageParams(): Record<string, unknown> | null {
|
|
64
110
|
const { config, params } = this;
|
|
65
111
|
const items = this.items;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const hasMore = getPath(this.body, config.hasMoreField);
|
|
69
|
-
if (hasMore === false) return null;
|
|
70
|
-
}
|
|
112
|
+
if (this.finished()) return null;
|
|
113
|
+
const backward = this.backward;
|
|
71
114
|
|
|
72
115
|
switch (config.style) {
|
|
73
116
|
case "cursor": {
|
|
74
|
-
const
|
|
117
|
+
const cursorParam = backward ? backward.cursorParam : config.cursorParam!;
|
|
118
|
+
const next = getPath(this.body, backward ? backward.cursorField : config.nextCursorField!);
|
|
75
119
|
if (next === undefined || next === null || next === "") return null;
|
|
76
|
-
if (next === params[
|
|
77
|
-
return { ...params, [
|
|
120
|
+
if (next === params[cursorParam]) return null;
|
|
121
|
+
return { ...params, [cursorParam]: next };
|
|
78
122
|
}
|
|
79
123
|
}
|
|
80
|
-
|
|
124
|
+
// URL styles: the next request is the URL itself. Pass page_url as the
|
|
125
|
+
// pageUrl request option to fetch that page.
|
|
126
|
+
const url = this.nextPageUrl();
|
|
127
|
+
if (url === null) return null;
|
|
128
|
+
// The URL is the whole request: its query can hold parameters the
|
|
129
|
+
// operation does not declare (GitHub's after), so it is passed as is.
|
|
130
|
+
return { page_url: url };
|
|
81
131
|
}
|
|
82
132
|
|
|
83
133
|
/** Fetch the next page, or null when this is the last one. Throws the typed error on failure. */
|
|
84
134
|
async getNextPage(): Promise<Page<Item, E> | null> {
|
|
135
|
+
const url = this.nextPageUrl();
|
|
136
|
+
if (url !== null) return this.fetchPage({}, url, this.before + this.items.length);
|
|
137
|
+
if (this.config.style === "nextUrl" || this.config.style === "link") return null;
|
|
85
138
|
const next = this.nextPageParams();
|
|
86
139
|
if (next === null) return null;
|
|
87
|
-
return this.fetchPage(next);
|
|
140
|
+
return this.fetchPage(next, undefined, this.before + this.items.length);
|
|
88
141
|
}
|
|
89
142
|
|
|
90
143
|
/** Iterate every item on this page and all following pages. */
|
|
@@ -96,6 +149,36 @@ export class Page<Item, E = unknown> {
|
|
|
96
149
|
}
|
|
97
150
|
}
|
|
98
151
|
|
|
152
|
+
/** Signals every style shares: has_more false, or the total reached. */
|
|
153
|
+
private finished(): boolean {
|
|
154
|
+
// Backward Relay pages end on hasPreviousPage, forward ones on hasNextPage.
|
|
155
|
+
const hasMoreField = this.backward?.hasMoreField ?? this.config.hasMoreField;
|
|
156
|
+
if (hasMoreField !== undefined && getPath(this.body, hasMoreField) === false) return true;
|
|
157
|
+
if (this.config.totalField !== undefined) {
|
|
158
|
+
const total = getPath(this.body, this.config.totalField);
|
|
159
|
+
if (typeof total === "number" && this.itemsThrough() >= total) return true;
|
|
160
|
+
}
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Items up to the end of this page: counted along a walk, or implied by
|
|
165
|
+
* the page's position when it was fetched on its own (an MCP nextPage). */
|
|
166
|
+
private itemsThrough(): number {
|
|
167
|
+
let before = this.before;
|
|
168
|
+
const { config, params } = this;
|
|
169
|
+
if (before === 0 && config.style === "offset" && config.offsetParam !== undefined) {
|
|
170
|
+
const offset = Number(params[config.offsetParam]);
|
|
171
|
+
if (Number.isFinite(offset) && offset > 0) before = offset;
|
|
172
|
+
}
|
|
173
|
+
if (before === 0 && config.style === "page" && config.pageParam !== undefined && config.limitParam !== undefined) {
|
|
174
|
+
const current = Number(params[config.pageParam]);
|
|
175
|
+
const limit = Number(params[config.limitParam]);
|
|
176
|
+
const first = config.firstPage ?? 1;
|
|
177
|
+
if (Number.isFinite(current) && Number.isFinite(limit) && limit > 0 && current > first) before = (current - first) * limit;
|
|
178
|
+
}
|
|
179
|
+
return before + this.items.length;
|
|
180
|
+
}
|
|
181
|
+
|
|
99
182
|
private looksLikeMore(items: Item[]): boolean {
|
|
100
183
|
if (items.length === 0) return false;
|
|
101
184
|
if (this.config.limitParam !== undefined) {
|
|
@@ -137,20 +220,48 @@ export function paginate<Item, E>(
|
|
|
137
220
|
): PagePromise<Item, E> {
|
|
138
221
|
const isGraphql = false
|
|
139
222
|
;
|
|
140
|
-
const fetchPage: FetchPage<Item, E> = async (params) => {
|
|
223
|
+
const fetchPage: FetchPage<Item, E> = async (params, url, before = 0) => {
|
|
141
224
|
let nextReq: CoreRequest = { ...req, query: params };
|
|
225
|
+
if (url !== undefined) {
|
|
226
|
+
// A next-page URL is followed verbatim, but only on the API's own
|
|
227
|
+
// origin: the request carries the client's credentials.
|
|
228
|
+
const base = new URL(core.config.baseUrl);
|
|
229
|
+
const target = new URL(url, base);
|
|
230
|
+
if (target.origin !== base.origin) {
|
|
231
|
+
throw new PaginationError("The next page is on another origin (" + target.origin + "); refusing to send credentials there. Fetch it yourself if you trust it.");
|
|
232
|
+
}
|
|
233
|
+
nextReq = { ...req, query: undefined, url: target.toString() };
|
|
234
|
+
}
|
|
142
235
|
const result = await core.request<unknown, E>(nextReq);
|
|
143
236
|
if (!result.ok) throw result.error instanceof Error ? result.error : new Error(String(result.error));
|
|
144
|
-
|
|
237
|
+
// A page without its item array is a contract break, not an empty page:
|
|
238
|
+
// iterating it would silently end the walk. A null array is empty.
|
|
239
|
+
const items = config.itemsField === "" ? result.data : getPath(result.data, config.itemsField);
|
|
240
|
+
if (!Array.isArray(items) && !(items === null && config.itemsField !== "")) {
|
|
241
|
+
throw new PaginationError(
|
|
242
|
+
(config.itemsField === "" ? "The list response is not an array" : "The list response has no " + JSON.stringify(config.itemsField) + " array")
|
|
243
|
+
+ ". Check the API response against the spec, or configure this operation's pagination.",
|
|
244
|
+
result.response,
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
return new Page<Item, E>(fetchPage, config, params, result.data, result.response, before);
|
|
145
248
|
};
|
|
146
249
|
const initial: Record<string, unknown> = {};
|
|
147
250
|
let seed = req.query ?? {};
|
|
148
251
|
for (const [k, v] of Object.entries(seed)) {
|
|
149
252
|
if (v !== undefined) initial[k] = v;
|
|
150
253
|
}
|
|
151
|
-
|
|
254
|
+
// Relay servers reject a connection query with neither `first` nor `last`.
|
|
255
|
+
const backwardLimit = config.backward?.limitParam;
|
|
256
|
+
if (config.defaultLimit !== undefined && config.limitParam !== undefined && initial[config.limitParam] === undefined
|
|
257
|
+
&& (backwardLimit === undefined || initial[backwardLimit] === undefined)) {
|
|
258
|
+
initial[config.limitParam] = config.defaultLimit;
|
|
259
|
+
}
|
|
260
|
+
const pageUrl = req.options?.pageUrl;
|
|
261
|
+
return new PagePromise<Item, E>(pageUrl !== undefined ? fetchPage({}, pageUrl) : fetchPage(initial));
|
|
152
262
|
}
|
|
153
263
|
|
|
264
|
+
|
|
154
265
|
function getPath(body: unknown, path: string): unknown {
|
|
155
266
|
let node: unknown = body;
|
|
156
267
|
for (const key of path.split(".")) {
|
|
@@ -30,12 +30,22 @@ function parseKey(value: string): Buffer {
|
|
|
30
30
|
return key;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
/** The OS item's service name: the command that owns it, then a digest of
|
|
34
|
+
* the session file, so each profile has its own key. Limited to a fixed
|
|
35
|
+
* alphabet because the macOS write passes it through security's command
|
|
36
|
+
* stream. */
|
|
37
|
+
export function credentialKeyService(owner: string, path: string): string {
|
|
38
|
+
const name = owner.replace(/[^A-Za-z0-9._-]/g, "-") || "cli";
|
|
39
|
+
return name + ".credentials." + createHash("sha256").update(resolve(path)).digest("hex");
|
|
40
|
+
}
|
|
41
|
+
|
|
33
42
|
/** A small wrapping key avoids OS item-size limits; session files are encrypted
|
|
34
|
-
* separately so refresh rotation can retain the atomic file/lock transaction.
|
|
35
|
-
|
|
43
|
+
* separately so refresh rotation can retain the atomic file/lock transaction.
|
|
44
|
+
* `owner` is the command name; the CLI and its local MCP server pass the same
|
|
45
|
+
* one so they share the key. */
|
|
46
|
+
export function nativeCredentialKeyStore(path: string, owner: string, platform: NodeJS.Platform = process.platform, run: NativeCommand = nativeCommand, environmentName = "CREDENTIAL_STORE"): CredentialKeyStore {
|
|
36
47
|
const failed = (name: string): never => unavailable(name, environmentName);
|
|
37
|
-
const
|
|
38
|
-
const service = "typeship.credentials." + identity;
|
|
48
|
+
const service = credentialKeyService(owner, path);
|
|
39
49
|
const account = "session-key";
|
|
40
50
|
if (platform === "darwin") {
|
|
41
51
|
const name = "macOS Keychain";
|
|
@@ -175,9 +185,9 @@ export function encryptedCredentialCodec(path: string, keyStore: CredentialKeySt
|
|
|
175
185
|
/** OS protection is the default. Plaintext storage is an explicit, separate
|
|
176
186
|
* store for environments where the owner accepts that tradeoff. Never migrate
|
|
177
187
|
* or fall back silently when an OS service is unavailable. */
|
|
178
|
-
export function createCredentialStore(directory: string, mode: string = "os", environmentName = "CREDENTIAL_STORE"): FileCredentialStore {
|
|
188
|
+
export function createCredentialStore(directory: string, owner: string, mode: string = "os", environmentName = "CREDENTIAL_STORE"): FileCredentialStore {
|
|
179
189
|
if (mode === "file") return new FileCredentialStore(join(directory, "credentials.json"));
|
|
180
190
|
if (mode !== "os") throw new CredentialStorageError(environmentName + " must be os or file.");
|
|
181
191
|
const path = join(directory, "credentials.enc");
|
|
182
|
-
return new FileCredentialStore(path, 40_000, encryptedCredentialCodec(path, nativeCredentialKeyStore(path, process.platform, nativeCommand, environmentName)));
|
|
192
|
+
return new FileCredentialStore(path, 40_000, encryptedCredentialCodec(path, nativeCredentialKeyStore(path, owner, process.platform, nativeCommand, environmentName)));
|
|
183
193
|
}
|
package/src/dates.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Relative dates for date / date-time arguments. Generated by
|
|
2
|
+
* Relative dates for date / date-time arguments. Generated by Typeship — https://typeship.dev
|
|
3
3
|
*
|
|
4
4
|
* Agents rarely know "now" and often do know "a week ago", so date-shaped
|
|
5
5
|
* arguments of the CLI and the MCP server accept, besides an absolute
|
package/src/errors.ts
CHANGED
|
@@ -1,138 +1,69 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Generated by
|
|
1
|
+
// Typeship — typed error classes.
|
|
2
|
+
// Generated by Typeship — https://typeship.dev
|
|
3
3
|
|
|
4
|
-
import { ApiError, type ResponseMeta } from "./core/http.js";
|
|
4
|
+
import { ApiError, RateLimitError, ServerError, type ResponseMeta } from "./core/http.js";
|
|
5
5
|
import type { ErrorModel, ErrorModelRead } from "./types.js";
|
|
6
6
|
|
|
7
|
-
export {
|
|
7
|
+
export {
|
|
8
|
+
ApiError,
|
|
9
|
+
SdkError,
|
|
10
|
+
PayloadError,
|
|
11
|
+
ResponseParseError,
|
|
12
|
+
TransportError,
|
|
13
|
+
UnexpectedApiError,
|
|
14
|
+
ValidationError,
|
|
15
|
+
BadRequestError,
|
|
16
|
+
ConflictError,
|
|
17
|
+
ForbiddenError,
|
|
18
|
+
NotFoundError,
|
|
19
|
+
NotModifiedError,
|
|
20
|
+
RateLimitError,
|
|
21
|
+
ServerError,
|
|
22
|
+
UnauthorizedError,
|
|
23
|
+
UnprocessableEntityError,
|
|
24
|
+
type RateLimitInfo,
|
|
25
|
+
type Violation,
|
|
26
|
+
} from "./core/http.js";
|
|
8
27
|
|
|
9
|
-
/**
|
|
10
|
-
|
|
11
|
-
* Raised for HTTP 400 responses.
|
|
12
|
-
*/
|
|
13
|
-
export class BadRequestError extends ApiError<400, ErrorModelRead> {
|
|
14
|
-
constructor(body: ErrorModelRead, response: ResponseMeta) {
|
|
15
|
-
super("The request body, Spec source, target selection, or package name is invalid.", 400, body, response);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Missing, invalid, expired, or revoked credentials.
|
|
21
|
-
* Raised for HTTP 401 responses.
|
|
22
|
-
*/
|
|
23
|
-
export class UnauthorizedError extends ApiError<401, ErrorModelRead> {
|
|
24
|
-
constructor(body: ErrorModelRead, response: ResponseMeta) {
|
|
25
|
-
super("Missing, invalid, expired, or revoked credentials.", 401, body, response);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* The credentials are valid but cannot act on the requested organization.
|
|
31
|
-
* Raised for HTTP 403 responses.
|
|
32
|
-
*/
|
|
33
|
-
export class ForbiddenError extends ApiError<403, ErrorModelRead> {
|
|
28
|
+
/** Raised for HTTP 402 responses. */
|
|
29
|
+
export class PaymentRequiredError extends ApiError<402, ErrorModelRead> {
|
|
34
30
|
constructor(body: ErrorModelRead, response: ResponseMeta) {
|
|
35
|
-
super("
|
|
31
|
+
super("HTTP 402", 402, body, response);
|
|
36
32
|
}
|
|
37
33
|
}
|
|
38
34
|
|
|
39
|
-
/**
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
export class ConflictError extends ApiError<409, ErrorModelRead> {
|
|
44
|
-
constructor(body: ErrorModelRead, response: ResponseMeta) {
|
|
45
|
-
super("The key identifies changed intent.", 409, body, response);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
35
|
+
/** @deprecated Use RateLimitError, raised for every 429 response. */
|
|
36
|
+
export const RateLimitedError = RateLimitError;
|
|
37
|
+
/** @deprecated Use RateLimitError, raised for every 429 response. */
|
|
38
|
+
export type RateLimitedError = RateLimitError<ErrorModelRead>;
|
|
48
39
|
|
|
49
|
-
/**
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
export class PayloadTooLargeError extends ApiError<413, ErrorModelRead> {
|
|
54
|
-
constructor(body: ErrorModelRead, response: ResponseMeta) {
|
|
55
|
-
super("Spec exceeds the 10MB limit.", 413, body, response);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
40
|
+
/** @deprecated Use ServerError, raised for every 5xx response. */
|
|
41
|
+
export const InternalServerError = ServerError;
|
|
42
|
+
/** @deprecated Use ServerError, raised for every 5xx response. */
|
|
43
|
+
export type InternalServerError = ServerError<ErrorModelRead>;
|
|
58
44
|
|
|
59
|
-
/**
|
|
60
|
-
|
|
61
|
-
* Raised for HTTP 422 responses.
|
|
62
|
-
*/
|
|
63
|
-
export class UnprocessableEntityError extends ApiError<422, ErrorModelRead> {
|
|
45
|
+
/** Raised for HTTP 412 responses. */
|
|
46
|
+
export class PreconditionFailedError extends ApiError<412, ErrorModelRead> {
|
|
64
47
|
constructor(body: ErrorModelRead, response: ResponseMeta) {
|
|
65
|
-
super("
|
|
48
|
+
super("HTTP 412", 412, body, response);
|
|
66
49
|
}
|
|
67
50
|
}
|
|
68
51
|
|
|
69
|
-
/**
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
*/
|
|
74
|
-
export class RateLimitedError extends ApiError<429, ErrorModelRead> {
|
|
75
|
-
constructor(body: ErrorModelRead, response: ResponseMeta) {
|
|
76
|
-
super("Too many requests, or an identical write is still in progress. Wait for Retry-After before retrying.", 429, body, response);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
52
|
+
/** @deprecated Use ServerError, raised for every 5xx response. */
|
|
53
|
+
export const BadGatewayError = ServerError;
|
|
54
|
+
/** @deprecated Use ServerError, raised for every 5xx response. */
|
|
55
|
+
export type BadGatewayError = ServerError<ErrorModelRead>;
|
|
79
56
|
|
|
80
|
-
/**
|
|
81
|
-
|
|
82
|
-
* Raised for HTTP 500 responses.
|
|
83
|
-
*/
|
|
84
|
-
export class InternalServerError extends ApiError<500, ErrorModelRead> {
|
|
57
|
+
/** Raised for HTTP 413 responses. */
|
|
58
|
+
export class PayloadTooLargeError extends ApiError<413, ErrorModelRead> {
|
|
85
59
|
constructor(body: ErrorModelRead, response: ResponseMeta) {
|
|
86
|
-
super("
|
|
60
|
+
super("HTTP 413", 413, body, response);
|
|
87
61
|
}
|
|
88
62
|
}
|
|
89
63
|
|
|
90
|
-
/**
|
|
91
|
-
* Unexpected error.
|
|
92
|
-
* Raised for "default" responses.
|
|
93
|
-
*/
|
|
64
|
+
/** Raised for "default" responses. */
|
|
94
65
|
export class ApiResponseError extends ApiError<number, ErrorModelRead> {
|
|
95
66
|
constructor(body: ErrorModelRead, response: ResponseMeta) {
|
|
96
|
-
super("
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* No such resource in this organization.
|
|
102
|
-
* Raised for HTTP 404 responses.
|
|
103
|
-
*/
|
|
104
|
-
export class NotFoundError extends ApiError<404, ErrorModelRead> {
|
|
105
|
-
constructor(body: ErrorModelRead, response: ResponseMeta) {
|
|
106
|
-
super("No such resource in this organization.", 404, body, response);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* The plan does not include another project or the requested target configuration.
|
|
112
|
-
* Raised for HTTP 402 responses.
|
|
113
|
-
*/
|
|
114
|
-
export class PaymentRequiredError extends ApiError<402, ErrorModelRead> {
|
|
115
|
-
constructor(body: ErrorModelRead, response: ResponseMeta) {
|
|
116
|
-
super("The plan does not include another project or the requested target configuration.", 402, body, response);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* The resource changed since the ETag supplied in If-Match. No write was applied.
|
|
122
|
-
* Raised for HTTP 412 responses.
|
|
123
|
-
*/
|
|
124
|
-
export class PreconditionFailedError extends ApiError<412, ErrorModelRead> {
|
|
125
|
-
constructor(body: ErrorModelRead, response: ResponseMeta) {
|
|
126
|
-
super("The resource changed since the ETag supplied in If-Match. No write was applied.", 412, body, response);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Dependent work failed while completing the request.
|
|
132
|
-
* Raised for HTTP 502 responses.
|
|
133
|
-
*/
|
|
134
|
-
export class BadGatewayError extends ApiError<502, ErrorModelRead> {
|
|
135
|
-
constructor(body: ErrorModelRead, response: ResponseMeta) {
|
|
136
|
-
super("Dependent work failed while completing the request.", 502, body, response);
|
|
67
|
+
super("HTTP " + response.status, response.status, body, response);
|
|
137
68
|
}
|
|
138
69
|
}
|
package/src/fields.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result projection shared by the generated CLI (--fields) and MCP server
|
|
3
|
+
* (the fields argument). Generated by Typeship — https://typeship.dev
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/** Keep only `paths` of a value: arrays item by item, objects by dotted
|
|
7
|
+
* path, including paths through arrays (`items.id` keeps each item's id);
|
|
8
|
+
* scalars untouched. */
|
|
9
|
+
export function projectFields(value: unknown, paths: string[][] | null): unknown {
|
|
10
|
+
if (paths === null) return value;
|
|
11
|
+
if (Array.isArray(value)) return value.map((v) => projectFields(v, paths));
|
|
12
|
+
if (value === null || typeof value !== "object") return value;
|
|
13
|
+
const groups = new Map<string, string[][]>();
|
|
14
|
+
for (const [key, ...rest] of paths) {
|
|
15
|
+
if (key === undefined) continue;
|
|
16
|
+
const group = groups.get(key);
|
|
17
|
+
if (group) group.push(rest); else groups.set(key, [rest]);
|
|
18
|
+
}
|
|
19
|
+
const out: Record<string, unknown> = {};
|
|
20
|
+
for (const [key, rests] of groups) {
|
|
21
|
+
const child = (value as Record<string, unknown>)[key];
|
|
22
|
+
if (child === undefined) continue;
|
|
23
|
+
if (rests.some((rest) => rest.length === 0)) out[key] = child;
|
|
24
|
+
else if (child !== null && typeof child === "object") out[key] = projectFields(child, rests);
|
|
25
|
+
}
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** A requested path that selected nothing, with the keys that exist where
|
|
30
|
+
* it stopped matching. */
|
|
31
|
+
export interface UnmatchedField {
|
|
32
|
+
path: string;
|
|
33
|
+
/** Keys present at the level where the path failed, first 40. */
|
|
34
|
+
available: string[];
|
|
35
|
+
/** The path segment that is not a key there, or null when the path runs
|
|
36
|
+
* into a scalar or the result is not an object. */
|
|
37
|
+
missing: string | null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The paths that match nothing in any item of `value`. A path matches when
|
|
42
|
+
* at least one item has it; reaching null or an empty array counts as a
|
|
43
|
+
* match, because the path may be right and the values merely empty. An
|
|
44
|
+
* empty result matches everything.
|
|
45
|
+
*/
|
|
46
|
+
export function unmatchedFields(value: unknown, paths: string[][]): UnmatchedField[] {
|
|
47
|
+
const out: UnmatchedField[] = [];
|
|
48
|
+
for (const path of paths) {
|
|
49
|
+
const miss = { depth: -1, keys: new Set<string>(), segment: null as string | null };
|
|
50
|
+
if (!pathMatches(value, path, 0, miss)) {
|
|
51
|
+
out.push({ path: path.join("."), available: [...miss.keys].slice(0, 40), missing: miss.segment });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function pathMatches(value: unknown, path: string[], index: number, miss: { depth: number; keys: Set<string>; segment: string | null }): boolean {
|
|
58
|
+
if (value === null || value === undefined) return true;
|
|
59
|
+
if (Array.isArray(value)) {
|
|
60
|
+
if (value.length === 0) return true;
|
|
61
|
+
let any = false;
|
|
62
|
+
for (const item of value) if (pathMatches(item, path, index, miss)) any = true;
|
|
63
|
+
return any;
|
|
64
|
+
}
|
|
65
|
+
if (index === path.length) return true;
|
|
66
|
+
if (typeof value !== "object") {
|
|
67
|
+
if (miss.depth < index) { miss.depth = index; miss.keys = new Set(); miss.segment = null; }
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
const key = path[index]!;
|
|
71
|
+
const record = value as Record<string, unknown>;
|
|
72
|
+
if (!Object.hasOwn(record, key)) {
|
|
73
|
+
if (miss.depth < index) { miss.depth = index; miss.keys = new Set(); miss.segment = key; }
|
|
74
|
+
if (miss.depth === index) for (const k of Object.keys(record)) miss.keys.add(k);
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
return pathMatches(record[key], path, index + 1, miss);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** The error message: the call went through, and for each unmatched path,
|
|
81
|
+
* what exists instead. */
|
|
82
|
+
export function unmatchedFieldsMessage(unmatched: UnmatchedField[], perItem: boolean): string {
|
|
83
|
+
return "The API call succeeded, but " + unmatched.map((u, index) => {
|
|
84
|
+
const where = perItem ? " in any item" : " in the result";
|
|
85
|
+
const why = u.missing !== null
|
|
86
|
+
? " (\"" + u.missing + "\" is not a key there)"
|
|
87
|
+
: " (it runs into a value that is not an object)";
|
|
88
|
+
const keys = u.available.length > 0 ? " Available keys: " + u.available.join(", ") + "." : "";
|
|
89
|
+
return (index === 0 ? "the" : "The") + " field path \"" + u.path + "\" matched nothing" + where + why + "." + keys;
|
|
90
|
+
}).join(" ");
|
|
91
|
+
}
|