@gusnips/sdkgen 0.2.3 → 0.2.5
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 +15 -2
- package/dist/transport.d.ts +16 -1
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +26 -13
- package/dist/transport.js.map +1 -1
- package/package.json +1 -1
- package/src/transport.test.ts +51 -11
- package/src/transport.ts +38 -14
package/README.md
CHANGED
|
@@ -199,8 +199,21 @@ export class Example extends GeneratedOperations {
|
|
|
199
199
|
| `mintKeys` | false | Makes up an idempotency key for a call that takes one, so it can try again. |
|
|
200
200
|
|
|
201
201
|
`failure` has the status (0 when no answer came back), the API's `error`, the `Retry-After` wait,
|
|
202
|
-
the request id, and the idempotency key the call went out with. A call that
|
|
203
|
-
sent again with that key, and the API answers from the first run.
|
|
202
|
+
the request id, the answer's `headers`, and the idempotency key the call went out with. A call that
|
|
203
|
+
may have run can be sent again with that key, and the API answers from the first run.
|
|
204
|
+
|
|
205
|
+
A request your SDK sends itself, such as a stream it opens with its own `fetch`, gets the same
|
|
206
|
+
`failure` from `failureOf`. Read the body first, because a body can be read only once:
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
if (!response.ok) {
|
|
210
|
+
const text = await response.text();
|
|
211
|
+
throw new ExampleError(failureOf({ method: "GET", path }, response, text));
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Pass `timeoutMs` too if that request had a deadline. A stream usually has none, and then
|
|
216
|
+
`failure.timeoutMs` is unset. It is always set when `failure.timedOut` is true.
|
|
204
217
|
|
|
205
218
|
A failed call is tried again:
|
|
206
219
|
|
package/dist/transport.d.ts
CHANGED
|
@@ -42,7 +42,11 @@ export interface Failure {
|
|
|
42
42
|
status: number;
|
|
43
43
|
/** No answer within `timeoutMs`. The call may still have run. */
|
|
44
44
|
timedOut: boolean;
|
|
45
|
-
|
|
45
|
+
/**
|
|
46
|
+
* How long the call could wait for an answer. Always set when `timedOut` is. A request with no
|
|
47
|
+
* deadline, such as a stream the SDK opens itself and reads through `failureOf`, leaves it unset.
|
|
48
|
+
*/
|
|
49
|
+
timeoutMs: number | undefined;
|
|
46
50
|
/** The envelope's `error`, when the answer carried one. */
|
|
47
51
|
error: EnvelopeError | undefined;
|
|
48
52
|
/** An answer that was not the envelope, such as a gateway's HTML page: its first 500 characters. */
|
|
@@ -51,6 +55,8 @@ export interface Failure {
|
|
|
51
55
|
retryAfterSecs: number | undefined;
|
|
52
56
|
/** The `x-request-id` header, for the API's support to find the call. */
|
|
53
57
|
requestId: string | undefined;
|
|
58
|
+
/** The answer's headers, for one your API adds to a refusal, such as what the call cost. */
|
|
59
|
+
headers: Headers | undefined;
|
|
54
60
|
/** The key the call was sent with. Retry with it, and a call that ran answers from its first run. */
|
|
55
61
|
idempotencyKey: string | undefined;
|
|
56
62
|
/** What fetch threw, when no answer came back. */
|
|
@@ -92,4 +98,13 @@ export interface Answer<T, M> {
|
|
|
92
98
|
* anything is sent.
|
|
93
99
|
*/
|
|
94
100
|
export declare function send<T = unknown, M = unknown>(transport: Transport, spec: RequestSpec, params?: object, opts?: RequestOptions): Promise<Answer<T, M>>;
|
|
101
|
+
/**
|
|
102
|
+
* The failure an answer describes, for a request the SDK sent itself, such as a stream it opens
|
|
103
|
+
* with its own `fetch`. Read the body first and pass its text: a body can be read only once.
|
|
104
|
+
* `send` reads its own answers through this too, so both come out the same. An answer never timed
|
|
105
|
+
* out, so `timeoutMs` is only a record of the deadline; leave it out when the request had none.
|
|
106
|
+
*/
|
|
107
|
+
export declare function failureOf(call: Pick<Failure, "method" | "path"> & {
|
|
108
|
+
timeoutMs?: number;
|
|
109
|
+
}, response: Response, text: string): Failure;
|
|
95
110
|
//# sourceMappingURL=transport.d.ts.map
|
package/dist/transport.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAsBA,uEAAuE;AACvE,MAAM,WAAW,WAAW;IAG1B,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;IACpD,4FAA4F;IAC5F,IAAI,EAAE,MAAM,CAAC;IACb,mGAAmG;IACnG,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,iGAAiG;IACjG,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,mDAAmD;AACnD,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,sFAAsF;IACtF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,6DAA6D;AAC7D,MAAM,WAAW,aAAa;IAC5B,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,SAAS,CAAC;IACpD,qEAAqE;IACrE,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,iFAAiF;AACjF,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC;IAC9B,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,gGAAgG;IAChG,MAAM,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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"}
|
package/dist/transport.js
CHANGED
|
@@ -152,6 +152,7 @@ async function once(fetchImpl, request, timeoutMs, signal) {
|
|
|
152
152
|
text: undefined,
|
|
153
153
|
retryAfterSecs: undefined,
|
|
154
154
|
requestId: undefined,
|
|
155
|
+
headers: undefined,
|
|
155
156
|
cause,
|
|
156
157
|
},
|
|
157
158
|
};
|
|
@@ -161,20 +162,32 @@ async function once(fetchImpl, request, timeoutMs, signal) {
|
|
|
161
162
|
// The one place the SDK takes the API's word for a type: the contract says what `data` is.
|
|
162
163
|
return { ok: true, answer: { data: body["data"], meta: body["meta"] } };
|
|
163
164
|
}
|
|
164
|
-
|
|
165
|
+
// ponytail: parses the body a second time, on a failure only, so a stream's refusal and a
|
|
166
|
+
// call's are read by one function.
|
|
167
|
+
return { ok: false, failure: failureOf(base, response, text) };
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* The failure an answer describes, for a request the SDK sent itself, such as a stream it opens
|
|
171
|
+
* with its own `fetch`. Read the body first and pass its text: a body can be read only once.
|
|
172
|
+
* `send` reads its own answers through this too, so both come out the same. An answer never timed
|
|
173
|
+
* out, so `timeoutMs` is only a record of the deadline; leave it out when the request had none.
|
|
174
|
+
*/
|
|
175
|
+
export function failureOf(call, response, text) {
|
|
176
|
+
const error = envelopeError(parse(text)?.["error"]);
|
|
165
177
|
return {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
+
method: call.method,
|
|
179
|
+
path: call.path,
|
|
180
|
+
timeoutMs: call.timeoutMs,
|
|
181
|
+
// A 2xx lands here only when its body is not JSON, which is not the API answering.
|
|
182
|
+
status: response.status,
|
|
183
|
+
timedOut: false,
|
|
184
|
+
error,
|
|
185
|
+
text: error === undefined && text !== "" ? text.slice(0, 500) : undefined,
|
|
186
|
+
retryAfterSecs: parseRetryAfter(response.headers.get("retry-after")),
|
|
187
|
+
requestId: response.headers.get("x-request-id") ?? undefined,
|
|
188
|
+
headers: response.headers,
|
|
189
|
+
idempotencyKey: undefined,
|
|
190
|
+
cause: undefined,
|
|
178
191
|
};
|
|
179
192
|
}
|
|
180
193
|
/** The body as a JSON object: `{}` when empty, `undefined` when it is not a JSON object. */
|
package/dist/transport.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gusnips/sdkgen",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"description": "The pieces of an SDK generator: copy your API's types with their comments, write a schema as a TypeScript type, and keep the output current.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/src/transport.test.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
8
8
|
import {
|
|
9
|
+
failureOf,
|
|
9
10
|
send,
|
|
10
11
|
type Failure,
|
|
11
12
|
type RequestOptions,
|
|
@@ -122,7 +123,7 @@ async function call(
|
|
|
122
123
|
return { calls, outcome };
|
|
123
124
|
}
|
|
124
125
|
|
|
125
|
-
function
|
|
126
|
+
function thrownFailure(outcome: Outcome): Failure {
|
|
126
127
|
if (outcome.ok || !(outcome.error instanceof SdkError)) {
|
|
127
128
|
throw new Error(`expected an SdkError, got ${JSON.stringify(outcome)}`);
|
|
128
129
|
}
|
|
@@ -272,7 +273,7 @@ describe("send: the idempotency key", () => {
|
|
|
272
273
|
transport: { mintKeys: true },
|
|
273
274
|
});
|
|
274
275
|
expect(calls[0]!.headers.get("idempotency-key")).toBe("k-1");
|
|
275
|
-
expect(
|
|
276
|
+
expect(thrownFailure(outcome).idempotencyKey).toBe("k-1");
|
|
276
277
|
});
|
|
277
278
|
|
|
278
279
|
it("sends no key on a call that takes none, even when minting", async () => {
|
|
@@ -353,16 +354,16 @@ describe("send: the request", () => {
|
|
|
353
354
|
const timeoutMs = vi.fn(() => 5000);
|
|
354
355
|
const hooked = await call(WRITE, [offline], { params: { a: 1 }, transport: { timeoutMs } });
|
|
355
356
|
expect(timeoutMs).toHaveBeenCalledWith(WRITE, { a: 1 });
|
|
356
|
-
expect(
|
|
357
|
+
expect(thrownFailure(hooked.outcome).timeoutMs).toBe(5000);
|
|
357
358
|
|
|
358
359
|
const own = await call(WRITE, [offline], {
|
|
359
360
|
opts: { timeoutMs: 700 },
|
|
360
361
|
transport: { timeoutMs },
|
|
361
362
|
});
|
|
362
|
-
expect(
|
|
363
|
+
expect(thrownFailure(own.outcome).timeoutMs).toBe(700);
|
|
363
364
|
|
|
364
365
|
const fallback = await call(WRITE, [offline]);
|
|
365
|
-
expect(
|
|
366
|
+
expect(thrownFailure(fallback.outcome).timeoutMs).toBe(30_000);
|
|
366
367
|
});
|
|
367
368
|
});
|
|
368
369
|
|
|
@@ -385,7 +386,7 @@ describe("send: the answer", () => {
|
|
|
385
386
|
details: { limit: 5 },
|
|
386
387
|
headers: { "retry-after": "60", "x-request-id": "req_1" },
|
|
387
388
|
});
|
|
388
|
-
const failure =
|
|
389
|
+
const failure = thrownFailure((await call(WRITE, [reply])).outcome);
|
|
389
390
|
expect(failure).toMatchObject({
|
|
390
391
|
method: "POST",
|
|
391
392
|
path: "/things",
|
|
@@ -400,7 +401,7 @@ describe("send: the answer", () => {
|
|
|
400
401
|
|
|
401
402
|
it("keeps the text of an answer that is not the envelope, a 2xx included", async () => {
|
|
402
403
|
const html = () => new Response("<html>Bad gateway</html>", { status: 502 });
|
|
403
|
-
const gateway =
|
|
404
|
+
const gateway = thrownFailure((await call(WRITE, [html])).outcome);
|
|
404
405
|
expect(gateway).toMatchObject({
|
|
405
406
|
status: 502,
|
|
406
407
|
error: undefined,
|
|
@@ -408,17 +409,56 @@ describe("send: the answer", () => {
|
|
|
408
409
|
});
|
|
409
410
|
|
|
410
411
|
const notJson = () => new Response("<html>Welcome</html>", { status: 200 });
|
|
411
|
-
const portal =
|
|
412
|
+
const portal = thrownFailure((await call(GET, [notJson])).outcome);
|
|
412
413
|
expect(portal).toMatchObject({ status: 200, text: "<html>Welcome</html>" });
|
|
413
414
|
});
|
|
414
415
|
|
|
416
|
+
it("hands over the answer's headers, and none when no answer came back", async () => {
|
|
417
|
+
const reply = refuse(422, {
|
|
418
|
+
code: "BLOCKED",
|
|
419
|
+
headers: { "x-request-cost": "0", "x-credits-remaining": "988" },
|
|
420
|
+
});
|
|
421
|
+
const failure = thrownFailure((await call(WRITE, [reply])).outcome);
|
|
422
|
+
expect(failure.headers?.get("x-request-cost")).toBe("0");
|
|
423
|
+
expect(failure.headers?.get("x-credits-remaining")).toBe("988");
|
|
424
|
+
expect(thrownFailure((await call(WRITE, [offline])).outcome).headers).toBeUndefined();
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
it("reads a refusal the SDK fetched itself the way it reads its own", async () => {
|
|
428
|
+
const reply = refuse(429, {
|
|
429
|
+
code: "RATE_LIMITED",
|
|
430
|
+
details: { retryAfterSecs: null },
|
|
431
|
+
headers: { "retry-after": "60", "x-request-id": "req_1" },
|
|
432
|
+
});
|
|
433
|
+
const sent = thrownFailure((await call(GET, [reply])).outcome);
|
|
434
|
+
const response = reply();
|
|
435
|
+
const read = failureOf(
|
|
436
|
+
{ method: "GET", path: "/things", timeoutMs: 30_000 },
|
|
437
|
+
response,
|
|
438
|
+
await response.text(),
|
|
439
|
+
);
|
|
440
|
+
expect(read).toEqual({ ...sent, headers: expect.any(Headers) });
|
|
441
|
+
expect(read.headers?.get("x-request-id")).toBe("req_1");
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
it("reads a refusal to a request that had no deadline, such as a stream", async () => {
|
|
445
|
+
const response = refuse(403, { code: "FORBIDDEN" })();
|
|
446
|
+
const read = failureOf(
|
|
447
|
+
{ method: "GET", path: "/jobs/j_1/stream" },
|
|
448
|
+
response,
|
|
449
|
+
await response.text(),
|
|
450
|
+
);
|
|
451
|
+
expect(read).toMatchObject({ status: 403, timedOut: false, error: { code: "FORBIDDEN" } });
|
|
452
|
+
expect(read.timeoutMs).toBeUndefined();
|
|
453
|
+
});
|
|
454
|
+
|
|
415
455
|
it("reports no answer as status 0, and says when it was the timeout", async () => {
|
|
416
456
|
const timeout: Reply = () => {
|
|
417
457
|
throw new DOMException("The operation timed out.", "TimeoutError");
|
|
418
458
|
};
|
|
419
|
-
const failure =
|
|
459
|
+
const failure = thrownFailure((await call(WRITE, [timeout])).outcome);
|
|
420
460
|
expect(failure).toMatchObject({ status: 0, timedOut: true });
|
|
421
|
-
expect(
|
|
461
|
+
expect(thrownFailure((await call(WRITE, [offline])).outcome)).toMatchObject({
|
|
422
462
|
status: 0,
|
|
423
463
|
timedOut: false,
|
|
424
464
|
});
|
|
@@ -476,6 +516,6 @@ describe("send: the caller's signal", () => {
|
|
|
476
516
|
throw new DOMException("The operation timed out.", "TimeoutError");
|
|
477
517
|
};
|
|
478
518
|
const { outcome } = await call(WRITE, [timeout], { opts: { signal: controller.signal } });
|
|
479
|
-
expect(
|
|
519
|
+
expect(thrownFailure(outcome)).toMatchObject({ status: 0, timedOut: true });
|
|
480
520
|
});
|
|
481
521
|
});
|
package/src/transport.ts
CHANGED
|
@@ -69,7 +69,11 @@ export interface Failure {
|
|
|
69
69
|
status: number;
|
|
70
70
|
/** No answer within `timeoutMs`. The call may still have run. */
|
|
71
71
|
timedOut: boolean;
|
|
72
|
-
|
|
72
|
+
/**
|
|
73
|
+
* How long the call could wait for an answer. Always set when `timedOut` is. A request with no
|
|
74
|
+
* deadline, such as a stream the SDK opens itself and reads through `failureOf`, leaves it unset.
|
|
75
|
+
*/
|
|
76
|
+
timeoutMs: number | undefined;
|
|
73
77
|
/** The envelope's `error`, when the answer carried one. */
|
|
74
78
|
error: EnvelopeError | undefined;
|
|
75
79
|
/** An answer that was not the envelope, such as a gateway's HTML page: its first 500 characters. */
|
|
@@ -78,6 +82,8 @@ export interface Failure {
|
|
|
78
82
|
retryAfterSecs: number | undefined;
|
|
79
83
|
/** The `x-request-id` header, for the API's support to find the call. */
|
|
80
84
|
requestId: string | undefined;
|
|
85
|
+
/** The answer's headers, for one your API adds to a refusal, such as what the call cost. */
|
|
86
|
+
headers: Headers | undefined;
|
|
81
87
|
/** The key the call was sent with. Retry with it, and a call that ran answers from its first run. */
|
|
82
88
|
idempotencyKey: string | undefined;
|
|
83
89
|
/** What fetch threw, when no answer came back. */
|
|
@@ -279,6 +285,7 @@ async function once<T, M>(
|
|
|
279
285
|
text: undefined,
|
|
280
286
|
retryAfterSecs: undefined,
|
|
281
287
|
requestId: undefined,
|
|
288
|
+
headers: undefined,
|
|
282
289
|
cause,
|
|
283
290
|
},
|
|
284
291
|
};
|
|
@@ -289,20 +296,37 @@ async function once<T, M>(
|
|
|
289
296
|
// The one place the SDK takes the API's word for a type: the contract says what `data` is.
|
|
290
297
|
return { ok: true, answer: { data: body["data"] as T, meta: body["meta"] as M | undefined } };
|
|
291
298
|
}
|
|
292
|
-
|
|
299
|
+
// ponytail: parses the body a second time, on a failure only, so a stream's refusal and a
|
|
300
|
+
// call's are read by one function.
|
|
301
|
+
return { ok: false, failure: failureOf(base, response, text) };
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* The failure an answer describes, for a request the SDK sent itself, such as a stream it opens
|
|
306
|
+
* with its own `fetch`. Read the body first and pass its text: a body can be read only once.
|
|
307
|
+
* `send` reads its own answers through this too, so both come out the same. An answer never timed
|
|
308
|
+
* out, so `timeoutMs` is only a record of the deadline; leave it out when the request had none.
|
|
309
|
+
*/
|
|
310
|
+
export function failureOf(
|
|
311
|
+
call: Pick<Failure, "method" | "path"> & { timeoutMs?: number },
|
|
312
|
+
response: Response,
|
|
313
|
+
text: string,
|
|
314
|
+
): Failure {
|
|
315
|
+
const error = envelopeError(parse(text)?.["error"]);
|
|
293
316
|
return {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
317
|
+
method: call.method,
|
|
318
|
+
path: call.path,
|
|
319
|
+
timeoutMs: call.timeoutMs,
|
|
320
|
+
// A 2xx lands here only when its body is not JSON, which is not the API answering.
|
|
321
|
+
status: response.status,
|
|
322
|
+
timedOut: false,
|
|
323
|
+
error,
|
|
324
|
+
text: error === undefined && text !== "" ? text.slice(0, 500) : undefined,
|
|
325
|
+
retryAfterSecs: parseRetryAfter(response.headers.get("retry-after")),
|
|
326
|
+
requestId: response.headers.get("x-request-id") ?? undefined,
|
|
327
|
+
headers: response.headers,
|
|
328
|
+
idempotencyKey: undefined,
|
|
329
|
+
cause: undefined,
|
|
306
330
|
};
|
|
307
331
|
}
|
|
308
332
|
|