@ycadence/sdk 0.1.0 → 0.2.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 +38 -0
- package/dist/client.d.ts +123 -122
- package/dist/client.d.ts.map +1 -1
- package/dist/errors.d.ts +100 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +457 -64
- package/dist/index.js.map +9 -4
- package/dist/operations.d.ts +128 -0
- package/dist/operations.d.ts.map +1 -0
- package/dist/pagination.d.ts +13 -0
- package/dist/pagination.d.ts.map +1 -0
- package/dist/types.d.ts +280 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/package.json +2 -2
- package/src/client.ts +393 -135
- package/src/errors.ts +144 -0
- package/src/index.ts +31 -4
- package/src/operations.ts +74 -0
- package/src/pagination.ts +36 -0
- package/src/types.ts +364 -0
- package/src/version.ts +11 -0
package/dist/index.js
CHANGED
|
@@ -1,126 +1,376 @@
|
|
|
1
|
-
// src/
|
|
1
|
+
// src/errors.ts
|
|
2
2
|
class YCadenceError extends Error {
|
|
3
3
|
status;
|
|
4
4
|
code;
|
|
5
5
|
issues;
|
|
6
|
-
|
|
6
|
+
requestId;
|
|
7
|
+
details;
|
|
8
|
+
retryAfter;
|
|
9
|
+
method;
|
|
10
|
+
path;
|
|
11
|
+
constructor(status, code, message, issues, extras = {}) {
|
|
7
12
|
super(message);
|
|
8
13
|
this.status = status;
|
|
9
14
|
this.code = code;
|
|
10
15
|
this.issues = issues;
|
|
11
16
|
this.name = "YCadenceError";
|
|
17
|
+
this.requestId = extras.requestId;
|
|
18
|
+
this.details = extras.details;
|
|
19
|
+
this.retryAfter = extras.retryAfter;
|
|
20
|
+
this.method = extras.method;
|
|
21
|
+
this.path = extras.path;
|
|
12
22
|
}
|
|
13
23
|
get retryable() {
|
|
14
|
-
return this.status === 429 || this.status >= 500;
|
|
24
|
+
return this.status === 0 || this.status === 408 || this.status === 429 || this.status >= 500;
|
|
15
25
|
}
|
|
16
26
|
}
|
|
17
27
|
|
|
28
|
+
class YCadenceValidationError extends YCadenceError {
|
|
29
|
+
name = "YCadenceValidationError";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
class YCadencePermissionError extends YCadenceError {
|
|
33
|
+
name = "YCadencePermissionError";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
class YCadenceNotFoundError extends YCadenceError {
|
|
37
|
+
name = "YCadenceNotFoundError";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
class YCadenceConflictError extends YCadenceError {
|
|
41
|
+
name = "YCadenceConflictError";
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
class YCadencePreconditionError extends YCadenceError {
|
|
45
|
+
name = "YCadencePreconditionError";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
class YCadenceRateLimitError extends YCadenceError {
|
|
49
|
+
name = "YCadenceRateLimitError";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
class YCadenceNetworkError extends YCadenceError {
|
|
53
|
+
name = "YCadenceNetworkError";
|
|
54
|
+
}
|
|
55
|
+
function errorClassFor(status) {
|
|
56
|
+
if (status === 0 || status === 408)
|
|
57
|
+
return YCadenceNetworkError;
|
|
58
|
+
if (status === 400 || status === 422)
|
|
59
|
+
return YCadenceValidationError;
|
|
60
|
+
if (status === 401 || status === 403)
|
|
61
|
+
return YCadencePermissionError;
|
|
62
|
+
if (status === 404)
|
|
63
|
+
return YCadenceNotFoundError;
|
|
64
|
+
if (status === 409)
|
|
65
|
+
return YCadenceConflictError;
|
|
66
|
+
if (status === 412)
|
|
67
|
+
return YCadencePreconditionError;
|
|
68
|
+
if (status === 429)
|
|
69
|
+
return YCadenceRateLimitError;
|
|
70
|
+
return YCadenceError;
|
|
71
|
+
}
|
|
72
|
+
function createError(status, code, message, issues, extras = {}) {
|
|
73
|
+
const ErrorClass = errorClassFor(status);
|
|
74
|
+
return new ErrorClass(status, code, message, issues, extras);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
class RequestIntercepted extends Error {
|
|
78
|
+
request;
|
|
79
|
+
name = "RequestIntercepted";
|
|
80
|
+
constructor(request) {
|
|
81
|
+
super(`${request.method} ${request.url} was not sent.`);
|
|
82
|
+
this.request = request;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// src/operations.ts
|
|
87
|
+
var OPERATIONS = {
|
|
88
|
+
health: { method: "GET", path: "/api/health" },
|
|
89
|
+
me: { method: "GET", path: "/api/v1/me" },
|
|
90
|
+
listProjects: { method: "GET", path: "/api/v1/projects" },
|
|
91
|
+
listWorkItems: { method: "GET", path: "/api/v1/projects/{projectId}/work-items" },
|
|
92
|
+
createWorkItem: { method: "POST", path: "/api/v1/projects/{projectId}/work-items" },
|
|
93
|
+
getWorkItem: { method: "GET", path: "/api/v1/work-items/{workItemId}" },
|
|
94
|
+
updateWorkItem: { method: "PATCH", path: "/api/v1/work-items/{workItemId}" },
|
|
95
|
+
deleteWorkItem: { method: "DELETE", path: "/api/v1/work-items/{workItemId}" },
|
|
96
|
+
listStates: { method: "GET", path: "/api/v1/projects/{projectId}/states" },
|
|
97
|
+
listLabels: { method: "GET", path: "/api/v1/projects/{projectId}/labels" },
|
|
98
|
+
createLabel: { method: "POST", path: "/api/v1/projects/{projectId}/labels" },
|
|
99
|
+
listTypes: { method: "GET", path: "/api/v1/projects/{projectId}/types" },
|
|
100
|
+
listCycles: { method: "GET", path: "/api/v1/projects/{projectId}/cycles" },
|
|
101
|
+
createCycle: { method: "POST", path: "/api/v1/projects/{projectId}/cycles" },
|
|
102
|
+
getCycleProgress: { method: "GET", path: "/api/v1/cycles/{cycleId}/progress" },
|
|
103
|
+
addWorkItemsToCycle: { method: "POST", path: "/api/v1/cycles/{cycleId}/work-items" },
|
|
104
|
+
listViews: { method: "GET", path: "/api/v1/projects/{projectId}/views" },
|
|
105
|
+
createView: { method: "POST", path: "/api/v1/projects/{projectId}/views" },
|
|
106
|
+
listIntake: { method: "GET", path: "/api/v1/projects/{projectId}/intake" },
|
|
107
|
+
submitIntake: { method: "POST", path: "/api/v1/projects/{projectId}/intake" },
|
|
108
|
+
triageIntake: { method: "POST", path: "/api/v1/intake-items/{intakeItemId}/triage" },
|
|
109
|
+
previewImport: { method: "POST", path: "/api/v1/projects/{projectId}/imports/preview" },
|
|
110
|
+
startImport: { method: "POST", path: "/api/v1/projects/{projectId}/imports" },
|
|
111
|
+
listImports: { method: "GET", path: "/api/v1/projects/{projectId}/imports" },
|
|
112
|
+
getImport: { method: "GET", path: "/api/v1/imports/{runId}" }
|
|
113
|
+
};
|
|
114
|
+
function isIdempotentMethod(method) {
|
|
115
|
+
return method === "GET" || method === "HEAD";
|
|
116
|
+
}
|
|
117
|
+
function buildPath(template, params = {}) {
|
|
118
|
+
return template.replace(/\{(\w+)\}/g, (_match, name) => {
|
|
119
|
+
const value = params[name];
|
|
120
|
+
if (value === undefined || value === "") {
|
|
121
|
+
throw new TypeError(`Missing path parameter "${name}" for ${template}.`);
|
|
122
|
+
}
|
|
123
|
+
return encodeURIComponent(value);
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// src/pagination.ts
|
|
128
|
+
async function* paginate(fetchPage, options = {}) {
|
|
129
|
+
let cursor = options.cursor;
|
|
130
|
+
let yielded = 0;
|
|
131
|
+
do {
|
|
132
|
+
const page = await fetchPage(cursor);
|
|
133
|
+
for (const item of page.items) {
|
|
134
|
+
if (options.maxItems !== undefined && yielded >= options.maxItems)
|
|
135
|
+
return;
|
|
136
|
+
yielded += 1;
|
|
137
|
+
yield item;
|
|
138
|
+
}
|
|
139
|
+
cursor = page.nextCursor ?? undefined;
|
|
140
|
+
} while (cursor !== undefined);
|
|
141
|
+
}
|
|
142
|
+
async function collect(iterable, maxItems = Number.POSITIVE_INFINITY) {
|
|
143
|
+
const items = [];
|
|
144
|
+
for await (const item of iterable) {
|
|
145
|
+
if (items.length >= maxItems)
|
|
146
|
+
break;
|
|
147
|
+
items.push(item);
|
|
148
|
+
}
|
|
149
|
+
return items;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// src/version.ts
|
|
153
|
+
var SDK_VERSION = "0.2.0";
|
|
154
|
+
|
|
155
|
+
// src/client.ts
|
|
156
|
+
var DEFAULT_RETRY = { retries: 2, baseDelayMs: 250, maxDelayMs: 8000 };
|
|
157
|
+
|
|
18
158
|
class YCadenceClient {
|
|
159
|
+
options;
|
|
19
160
|
baseUrl;
|
|
20
161
|
apiKey;
|
|
21
162
|
identityToken;
|
|
22
163
|
timeoutMs;
|
|
23
164
|
doFetch;
|
|
165
|
+
retry;
|
|
166
|
+
userAgent;
|
|
167
|
+
onResponse;
|
|
168
|
+
sleep;
|
|
24
169
|
constructor(options) {
|
|
170
|
+
this.options = options;
|
|
25
171
|
this.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
26
172
|
this.apiKey = options.apiKey;
|
|
27
173
|
this.identityToken = options.identityToken;
|
|
28
174
|
this.timeoutMs = options.timeoutMs ?? 30000;
|
|
29
|
-
this.doFetch = options.fetch ?? globalThis.fetch;
|
|
175
|
+
this.doFetch = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
176
|
+
this.retry = { ...DEFAULT_RETRY, ...options.retry };
|
|
177
|
+
this.userAgent = [options.userAgent, `ycadence-sdk/${SDK_VERSION}`].filter((part) => part !== undefined && part !== "").join(" ");
|
|
178
|
+
this.onResponse = options.onResponse;
|
|
179
|
+
this.sleep = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
180
|
+
}
|
|
181
|
+
withOptions(overrides) {
|
|
182
|
+
return new YCadenceClient({ ...this.options, ...overrides });
|
|
183
|
+
}
|
|
184
|
+
get fetchImplementation() {
|
|
185
|
+
return this.doFetch;
|
|
30
186
|
}
|
|
31
187
|
async health() {
|
|
32
|
-
return this.
|
|
33
|
-
}
|
|
34
|
-
async
|
|
35
|
-
|
|
36
|
-
if (options.filter !== undefined)
|
|
37
|
-
query.set("filter", JSON.stringify(options.filter));
|
|
38
|
-
if (options.limit !== undefined)
|
|
39
|
-
query.set("limit", String(options.limit));
|
|
40
|
-
if (options.cursor !== undefined)
|
|
41
|
-
query.set("cursor", options.cursor);
|
|
42
|
-
return this.request("GET", `/api/v1/projects/${projectId}/work-items?${query}`);
|
|
43
|
-
}
|
|
44
|
-
async* iterateWorkItems(projectId, options = {}) {
|
|
45
|
-
let cursor;
|
|
46
|
-
do {
|
|
47
|
-
const page = await this.listWorkItems(projectId, {
|
|
48
|
-
filter: options.filter,
|
|
49
|
-
limit: options.pageSize ?? 100,
|
|
50
|
-
cursor
|
|
51
|
-
});
|
|
52
|
-
for (const item of page.items)
|
|
53
|
-
yield item;
|
|
54
|
-
cursor = page.nextCursor ?? undefined;
|
|
55
|
-
} while (cursor !== undefined);
|
|
188
|
+
return this.call("health");
|
|
189
|
+
}
|
|
190
|
+
async me(options) {
|
|
191
|
+
return this.call("me", {}, undefined, undefined, options);
|
|
56
192
|
}
|
|
57
|
-
async
|
|
58
|
-
return this.
|
|
193
|
+
async listProjects(query = {}, options) {
|
|
194
|
+
return this.call("listProjects", {}, query, undefined, options);
|
|
59
195
|
}
|
|
60
|
-
async
|
|
61
|
-
return this.
|
|
196
|
+
async listWorkItems(projectId, query = {}, options) {
|
|
197
|
+
return this.call("listWorkItems", { projectId }, {
|
|
198
|
+
filter: query.filter === undefined ? undefined : JSON.stringify(query.filter),
|
|
199
|
+
limit: query.limit,
|
|
200
|
+
cursor: query.cursor,
|
|
201
|
+
state: query.state,
|
|
202
|
+
priority: query.priority
|
|
203
|
+
}, undefined, options);
|
|
62
204
|
}
|
|
63
|
-
|
|
64
|
-
return this.
|
|
205
|
+
iterateWorkItems(projectId, options = {}) {
|
|
206
|
+
return paginate((cursor) => this.listWorkItems(projectId, {
|
|
207
|
+
filter: options.filter,
|
|
208
|
+
limit: options.pageSize ?? 100,
|
|
209
|
+
cursor
|
|
210
|
+
}));
|
|
65
211
|
}
|
|
66
|
-
async
|
|
67
|
-
|
|
212
|
+
async createWorkItem(projectId, body, options) {
|
|
213
|
+
return this.call("createWorkItem", { projectId }, undefined, body, options);
|
|
68
214
|
}
|
|
69
|
-
async
|
|
70
|
-
return this.
|
|
215
|
+
async getWorkItem(workItemId, options) {
|
|
216
|
+
return this.call("getWorkItem", { workItemId }, undefined, undefined, options);
|
|
71
217
|
}
|
|
72
|
-
async
|
|
73
|
-
return this.
|
|
218
|
+
async updateWorkItem(workItemId, body, options) {
|
|
219
|
+
return this.call("updateWorkItem", { workItemId }, undefined, body, options);
|
|
74
220
|
}
|
|
75
|
-
async
|
|
76
|
-
|
|
221
|
+
async deleteWorkItem(workItemId, options) {
|
|
222
|
+
await this.call("deleteWorkItem", { workItemId }, undefined, undefined, options);
|
|
77
223
|
}
|
|
78
|
-
async
|
|
79
|
-
return this.
|
|
224
|
+
async listStates(projectId, options) {
|
|
225
|
+
return this.call("listStates", { projectId }, undefined, undefined, options);
|
|
80
226
|
}
|
|
81
|
-
async
|
|
82
|
-
return this.
|
|
227
|
+
async listLabels(projectId, options) {
|
|
228
|
+
return this.call("listLabels", { projectId }, undefined, undefined, options);
|
|
83
229
|
}
|
|
84
|
-
async
|
|
85
|
-
return this.
|
|
230
|
+
async createLabel(projectId, body, options) {
|
|
231
|
+
return this.call("createLabel", { projectId }, undefined, body, options);
|
|
86
232
|
}
|
|
87
|
-
async
|
|
88
|
-
return this.
|
|
233
|
+
async listTypes(projectId, options) {
|
|
234
|
+
return this.call("listTypes", { projectId }, undefined, undefined, options);
|
|
89
235
|
}
|
|
90
|
-
async
|
|
91
|
-
return this.
|
|
236
|
+
async listCycles(projectId, options) {
|
|
237
|
+
return this.call("listCycles", { projectId }, undefined, undefined, options);
|
|
92
238
|
}
|
|
93
|
-
async
|
|
239
|
+
async createCycle(projectId, body, options) {
|
|
240
|
+
return this.call("createCycle", { projectId }, undefined, body, options);
|
|
241
|
+
}
|
|
242
|
+
async getCycleProgress(cycleId, options) {
|
|
243
|
+
return this.call("getCycleProgress", { cycleId }, undefined, undefined, options);
|
|
244
|
+
}
|
|
245
|
+
async addWorkItemsToCycle(cycleId, workItemIds, options) {
|
|
246
|
+
return this.call("addWorkItemsToCycle", { cycleId }, undefined, { workItemIds }, options);
|
|
247
|
+
}
|
|
248
|
+
async listViews(projectId, options) {
|
|
249
|
+
return this.call("listViews", { projectId }, undefined, undefined, options);
|
|
250
|
+
}
|
|
251
|
+
async createView(projectId, body, options) {
|
|
252
|
+
return this.call("createView", { projectId }, undefined, body, options);
|
|
253
|
+
}
|
|
254
|
+
async listIntake(projectId, query = {}, options) {
|
|
255
|
+
return this.call("listIntake", { projectId }, query, undefined, options);
|
|
256
|
+
}
|
|
257
|
+
async submitIntake(projectId, body, options) {
|
|
258
|
+
return this.call("submitIntake", { projectId }, undefined, body, options);
|
|
259
|
+
}
|
|
260
|
+
async triageIntake(intakeItemId, body, options) {
|
|
261
|
+
return this.call("triageIntake", { intakeItemId }, undefined, body, options);
|
|
262
|
+
}
|
|
263
|
+
async previewImport(projectId, body, options) {
|
|
264
|
+
return this.call("previewImport", { projectId }, undefined, body, options);
|
|
265
|
+
}
|
|
266
|
+
async startImport(projectId, body, options) {
|
|
267
|
+
return this.call("startImport", { projectId }, undefined, body, options);
|
|
268
|
+
}
|
|
269
|
+
async getImport(runId, options) {
|
|
270
|
+
return this.call("getImport", { runId }, undefined, undefined, options);
|
|
271
|
+
}
|
|
272
|
+
async listImports(projectId, options) {
|
|
273
|
+
return this.call("listImports", { projectId }, undefined, undefined, options);
|
|
274
|
+
}
|
|
275
|
+
async call(operation, params = {}, query, body, options = {}) {
|
|
276
|
+
const { method, path } = OPERATIONS[operation];
|
|
277
|
+
let url = buildPath(path, params);
|
|
278
|
+
if (query !== undefined) {
|
|
279
|
+
const search = new URLSearchParams;
|
|
280
|
+
for (const [name, value] of Object.entries(query)) {
|
|
281
|
+
if (value !== undefined)
|
|
282
|
+
search.set(name, String(value));
|
|
283
|
+
}
|
|
284
|
+
const encoded = search.toString();
|
|
285
|
+
if (encoded.length > 0)
|
|
286
|
+
url += `?${encoded}`;
|
|
287
|
+
}
|
|
288
|
+
return this.request(method, url, body, options);
|
|
289
|
+
}
|
|
290
|
+
async request(method, path, body, options) {
|
|
291
|
+
const retryable = isIdempotentMethod(method) || options.idempotencyKey !== undefined;
|
|
292
|
+
const retries = retryable ? options.retries ?? this.retry.retries : 0;
|
|
293
|
+
for (let attempt = 0;; attempt += 1) {
|
|
294
|
+
try {
|
|
295
|
+
return await this.send(method, path, body, options, attempt);
|
|
296
|
+
} catch (error) {
|
|
297
|
+
const failure = error;
|
|
298
|
+
const again = attempt < retries && typeof failure.retryable === "boolean" && (failure.retryable || failure.code === "request_in_progress");
|
|
299
|
+
if (!again)
|
|
300
|
+
throw error;
|
|
301
|
+
await this.sleep(this.backoff(attempt, failure.retryAfter));
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
backoff(attempt, retryAfter) {
|
|
306
|
+
if (retryAfter !== undefined)
|
|
307
|
+
return Math.min(retryAfter * 1000, this.retry.maxDelayMs);
|
|
308
|
+
const exponential = this.retry.baseDelayMs * 2 ** attempt;
|
|
309
|
+
const jittered = exponential / 2 + Math.random() * (exponential / 2);
|
|
310
|
+
return Math.min(jittered, this.retry.maxDelayMs);
|
|
311
|
+
}
|
|
312
|
+
async send(method, path, body, options, attempt) {
|
|
94
313
|
const controller = new AbortController;
|
|
95
314
|
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
315
|
+
const abort = () => controller.abort();
|
|
316
|
+
options.signal?.addEventListener("abort", abort);
|
|
317
|
+
const started = Date.now();
|
|
96
318
|
let response;
|
|
97
319
|
try {
|
|
98
320
|
const identityToken = typeof this.identityToken === "function" ? await this.identityToken() : this.identityToken;
|
|
321
|
+
const headers = {
|
|
322
|
+
"x-api-key": this.apiKey,
|
|
323
|
+
...identityToken === undefined || identityToken.length === 0 ? {} : { Authorization: `Bearer ${identityToken}` },
|
|
324
|
+
...body === undefined ? {} : { "Content-Type": "application/json" },
|
|
325
|
+
Accept: "application/json",
|
|
326
|
+
"User-Agent": this.userAgent
|
|
327
|
+
};
|
|
328
|
+
if (options.idempotencyKey !== undefined && !isIdempotentMethod(method)) {
|
|
329
|
+
headers["Idempotency-Key"] = options.idempotencyKey;
|
|
330
|
+
}
|
|
331
|
+
if (options.expectedVersion !== undefined && !isIdempotentMethod(method)) {
|
|
332
|
+
headers["If-Match"] = `"${String(options.expectedVersion).replace(/"/g, "")}"`;
|
|
333
|
+
}
|
|
99
334
|
response = await this.doFetch(`${this.baseUrl}${path}`, {
|
|
100
335
|
method,
|
|
101
|
-
headers
|
|
102
|
-
"x-api-key": this.apiKey,
|
|
103
|
-
...identityToken === undefined || identityToken.length === 0 ? {} : { Authorization: `Bearer ${identityToken}` },
|
|
104
|
-
...body === undefined ? {} : { "Content-Type": "application/json" }
|
|
105
|
-
},
|
|
336
|
+
headers,
|
|
106
337
|
body: body === undefined ? undefined : JSON.stringify(body),
|
|
107
338
|
signal: controller.signal
|
|
108
339
|
});
|
|
109
340
|
} catch (error) {
|
|
341
|
+
if (error instanceof RequestIntercepted)
|
|
342
|
+
throw error;
|
|
110
343
|
if (error instanceof Error && error.name === "AbortError") {
|
|
111
|
-
throw
|
|
344
|
+
throw createError(408, "timeout", `${method} ${path} timed out after ${this.timeoutMs}ms.`, undefined, { method, path });
|
|
112
345
|
}
|
|
113
|
-
throw
|
|
346
|
+
throw createError(0, "network_error", `Could not reach ${this.baseUrl}: ${error instanceof Error ? error.message : "unknown error"}`, undefined, { method, path });
|
|
114
347
|
} finally {
|
|
115
348
|
clearTimeout(timer);
|
|
349
|
+
options.signal?.removeEventListener("abort", abort);
|
|
116
350
|
}
|
|
351
|
+
const headerRequestId = response.headers?.get?.("x-request-id") ?? undefined;
|
|
352
|
+
this.onResponse?.({
|
|
353
|
+
method,
|
|
354
|
+
path,
|
|
355
|
+
status: response.status,
|
|
356
|
+
requestId: headerRequestId,
|
|
357
|
+
durationMs: Date.now() - started,
|
|
358
|
+
attempt,
|
|
359
|
+
replayed: response.headers?.get?.("idempotent-replayed") === "true"
|
|
360
|
+
});
|
|
117
361
|
if (response.status === 204)
|
|
118
362
|
return;
|
|
119
363
|
const text = await response.text();
|
|
120
364
|
const parsed = text.length === 0 ? undefined : safeJson(text);
|
|
121
365
|
if (!response.ok) {
|
|
122
|
-
const failure = parsed
|
|
123
|
-
throw
|
|
366
|
+
const failure = isRecord(parsed) ? parsed : {};
|
|
367
|
+
throw createError(response.status, typeof failure.code === "string" ? failure.code : codeForStatus(response.status), typeof failure.error === "string" ? failure.error : `${method} ${path} failed with ${response.status}.`, failure.issues, {
|
|
368
|
+
requestId: failure.requestId ?? headerRequestId,
|
|
369
|
+
details: failure.details,
|
|
370
|
+
retryAfter: parseRetryAfter(response.headers?.get?.("retry-after") ?? null),
|
|
371
|
+
method,
|
|
372
|
+
path
|
|
373
|
+
});
|
|
124
374
|
}
|
|
125
375
|
return parsed;
|
|
126
376
|
}
|
|
@@ -132,14 +382,157 @@ function safeJson(text) {
|
|
|
132
382
|
return;
|
|
133
383
|
}
|
|
134
384
|
}
|
|
385
|
+
function isRecord(value) {
|
|
386
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
387
|
+
}
|
|
388
|
+
function codeForStatus(status) {
|
|
389
|
+
const codes = {
|
|
390
|
+
400: "bad_request",
|
|
391
|
+
401: "unauthorized",
|
|
392
|
+
403: "forbidden",
|
|
393
|
+
404: "not_found",
|
|
394
|
+
409: "conflict",
|
|
395
|
+
412: "precondition_failed",
|
|
396
|
+
422: "validation_failed",
|
|
397
|
+
429: "rate_limited"
|
|
398
|
+
};
|
|
399
|
+
return codes[status] ?? (status >= 500 ? "server_error" : "unknown");
|
|
400
|
+
}
|
|
401
|
+
function parseRetryAfter(value, now = Date.now()) {
|
|
402
|
+
if (value === null || value.trim() === "")
|
|
403
|
+
return;
|
|
404
|
+
const seconds = Number(value);
|
|
405
|
+
if (Number.isFinite(seconds))
|
|
406
|
+
return Math.max(0, seconds);
|
|
407
|
+
const date = Date.parse(value);
|
|
408
|
+
if (Number.isNaN(date))
|
|
409
|
+
return;
|
|
410
|
+
return Math.max(0, Math.ceil((date - now) / 1000));
|
|
411
|
+
}
|
|
135
412
|
var DevPulseClient = YCadenceClient;
|
|
136
413
|
var DevPulseError = YCadenceError;
|
|
414
|
+
// src/types.ts
|
|
415
|
+
var PRIORITIES = ["urgent", "high", "medium", "low", "none"];
|
|
416
|
+
var WORK_ITEM_FIELDS = {
|
|
417
|
+
id: true,
|
|
418
|
+
key: true,
|
|
419
|
+
sequenceId: true,
|
|
420
|
+
name: true,
|
|
421
|
+
priority: true,
|
|
422
|
+
stateId: true,
|
|
423
|
+
assigneeIds: true,
|
|
424
|
+
labelIds: true,
|
|
425
|
+
parentId: true,
|
|
426
|
+
sortOrder: true,
|
|
427
|
+
createdAt: true,
|
|
428
|
+
projectId: true,
|
|
429
|
+
version: true
|
|
430
|
+
};
|
|
431
|
+
var WORK_ITEM_DETAIL_FIELDS = {
|
|
432
|
+
...WORK_ITEM_FIELDS,
|
|
433
|
+
description: true,
|
|
434
|
+
startDate: true,
|
|
435
|
+
targetDate: true,
|
|
436
|
+
archivedAt: true,
|
|
437
|
+
updatedAt: true
|
|
438
|
+
};
|
|
439
|
+
var STATE_FIELDS = {
|
|
440
|
+
id: true,
|
|
441
|
+
name: true,
|
|
442
|
+
group: true,
|
|
443
|
+
color: true,
|
|
444
|
+
description: true,
|
|
445
|
+
sequence: true,
|
|
446
|
+
isDefault: true
|
|
447
|
+
};
|
|
448
|
+
var LABEL_FIELDS = {
|
|
449
|
+
id: true,
|
|
450
|
+
name: true,
|
|
451
|
+
color: true,
|
|
452
|
+
description: true,
|
|
453
|
+
sortOrder: true
|
|
454
|
+
};
|
|
455
|
+
var CYCLE_FIELDS = {
|
|
456
|
+
id: true,
|
|
457
|
+
name: true,
|
|
458
|
+
description: true,
|
|
459
|
+
startDate: true,
|
|
460
|
+
endDate: true,
|
|
461
|
+
status: true,
|
|
462
|
+
sortOrder: true,
|
|
463
|
+
completedAt: true,
|
|
464
|
+
archivedAt: true
|
|
465
|
+
};
|
|
466
|
+
var VIEW_FIELDS = {
|
|
467
|
+
id: true,
|
|
468
|
+
name: true,
|
|
469
|
+
description: true,
|
|
470
|
+
filter: true,
|
|
471
|
+
display: true,
|
|
472
|
+
access: true,
|
|
473
|
+
ownerId: true,
|
|
474
|
+
sortOrder: true
|
|
475
|
+
};
|
|
476
|
+
var INTAKE_ITEM_FIELDS = {
|
|
477
|
+
id: true,
|
|
478
|
+
workItemId: true,
|
|
479
|
+
status: true,
|
|
480
|
+
source: true,
|
|
481
|
+
sourceEmail: true,
|
|
482
|
+
snoozedUntil: true,
|
|
483
|
+
submittedAt: true
|
|
484
|
+
};
|
|
485
|
+
var IMPORT_RUN_FIELDS = {
|
|
486
|
+
id: true,
|
|
487
|
+
source: true,
|
|
488
|
+
status: true,
|
|
489
|
+
totalItems: true,
|
|
490
|
+
createdCount: true,
|
|
491
|
+
updatedCount: true,
|
|
492
|
+
cursor: true,
|
|
493
|
+
skipped: true,
|
|
494
|
+
unmatched: true,
|
|
495
|
+
error: true,
|
|
496
|
+
startedAt: true,
|
|
497
|
+
finishedAt: true,
|
|
498
|
+
createdAt: true
|
|
499
|
+
};
|
|
500
|
+
var PENDING_FIELDS = {
|
|
501
|
+
workItem: ["projectId", "version"]
|
|
502
|
+
};
|
|
137
503
|
export {
|
|
504
|
+
CYCLE_FIELDS,
|
|
138
505
|
DevPulseClient,
|
|
139
506
|
DevPulseError,
|
|
507
|
+
IMPORT_RUN_FIELDS,
|
|
508
|
+
INTAKE_ITEM_FIELDS,
|
|
509
|
+
LABEL_FIELDS,
|
|
510
|
+
OPERATIONS,
|
|
511
|
+
PENDING_FIELDS,
|
|
512
|
+
PRIORITIES,
|
|
513
|
+
RequestIntercepted,
|
|
514
|
+
SDK_VERSION,
|
|
515
|
+
STATE_FIELDS,
|
|
516
|
+
VIEW_FIELDS,
|
|
517
|
+
WORK_ITEM_DETAIL_FIELDS,
|
|
518
|
+
WORK_ITEM_FIELDS,
|
|
140
519
|
YCadenceClient,
|
|
141
|
-
|
|
520
|
+
YCadenceConflictError,
|
|
521
|
+
YCadenceError,
|
|
522
|
+
YCadenceNetworkError,
|
|
523
|
+
YCadenceNotFoundError,
|
|
524
|
+
YCadencePermissionError,
|
|
525
|
+
YCadencePreconditionError,
|
|
526
|
+
YCadenceRateLimitError,
|
|
527
|
+
YCadenceValidationError,
|
|
528
|
+
buildPath,
|
|
529
|
+
collect,
|
|
530
|
+
createError,
|
|
531
|
+
errorClassFor,
|
|
532
|
+
isIdempotentMethod,
|
|
533
|
+
paginate,
|
|
534
|
+
parseRetryAfter
|
|
142
535
|
};
|
|
143
536
|
|
|
144
|
-
//# debugId=
|
|
537
|
+
//# debugId=C4191F8D9028EF8064756E2164756E21
|
|
145
538
|
//# sourceMappingURL=index.js.map
|