@gusnips/sdkgen 0.2.3 → 0.2.4
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 +12 -2
- package/dist/transport.d.ts +8 -0
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +25 -13
- package/dist/transport.js.map +1 -1
- package/package.json +1 -1
- package/src/transport.test.ts +40 -11
- package/src/transport.ts +32 -13
package/README.md
CHANGED
|
@@ -199,8 +199,18 @@ 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, timeoutMs }, response, text));
|
|
212
|
+
}
|
|
213
|
+
```
|
|
204
214
|
|
|
205
215
|
A failed call is tried again:
|
|
206
216
|
|
package/dist/transport.d.ts
CHANGED
|
@@ -51,6 +51,8 @@ export interface Failure {
|
|
|
51
51
|
retryAfterSecs: number | undefined;
|
|
52
52
|
/** The `x-request-id` header, for the API's support to find the call. */
|
|
53
53
|
requestId: string | undefined;
|
|
54
|
+
/** The answer's headers, for one your API adds to a refusal, such as what the call cost. */
|
|
55
|
+
headers: Headers | undefined;
|
|
54
56
|
/** The key the call was sent with. Retry with it, and a call that ran answers from its first run. */
|
|
55
57
|
idempotencyKey: string | undefined;
|
|
56
58
|
/** What fetch threw, when no answer came back. */
|
|
@@ -92,4 +94,10 @@ export interface Answer<T, M> {
|
|
|
92
94
|
* anything is sent.
|
|
93
95
|
*/
|
|
94
96
|
export declare function send<T = unknown, M = unknown>(transport: Transport, spec: RequestSpec, params?: object, opts?: RequestOptions): Promise<Answer<T, M>>;
|
|
97
|
+
/**
|
|
98
|
+
* The failure an answer describes, for a request the SDK sent itself, such as a stream it opens
|
|
99
|
+
* with its own `fetch`. Read the body first and pass its text: a body can be read only once.
|
|
100
|
+
* `send` reads its own answers through this too, so both come out the same.
|
|
101
|
+
*/
|
|
102
|
+
export declare function failureOf(call: Pick<Failure, "method" | "path" | "timeoutMs">, response: Response, text: string): Failure;
|
|
95
103
|
//# 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":"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"}
|
|
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,31 @@ 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.
|
|
173
|
+
*/
|
|
174
|
+
export function failureOf(call, response, text) {
|
|
175
|
+
const error = envelopeError(parse(text)?.["error"]);
|
|
165
176
|
return {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
177
|
+
method: call.method,
|
|
178
|
+
path: call.path,
|
|
179
|
+
timeoutMs: call.timeoutMs,
|
|
180
|
+
// A 2xx lands here only when its body is not JSON, which is not the API answering.
|
|
181
|
+
status: response.status,
|
|
182
|
+
timedOut: false,
|
|
183
|
+
error,
|
|
184
|
+
text: error === undefined && text !== "" ? text.slice(0, 500) : undefined,
|
|
185
|
+
retryAfterSecs: parseRetryAfter(response.headers.get("retry-after")),
|
|
186
|
+
requestId: response.headers.get("x-request-id") ?? undefined,
|
|
187
|
+
headers: response.headers,
|
|
188
|
+
idempotencyKey: undefined,
|
|
189
|
+
cause: undefined,
|
|
178
190
|
};
|
|
179
191
|
}
|
|
180
192
|
/** 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.4",
|
|
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,45 @@ 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
|
+
|
|
415
444
|
it("reports no answer as status 0, and says when it was the timeout", async () => {
|
|
416
445
|
const timeout: Reply = () => {
|
|
417
446
|
throw new DOMException("The operation timed out.", "TimeoutError");
|
|
418
447
|
};
|
|
419
|
-
const failure =
|
|
448
|
+
const failure = thrownFailure((await call(WRITE, [timeout])).outcome);
|
|
420
449
|
expect(failure).toMatchObject({ status: 0, timedOut: true });
|
|
421
|
-
expect(
|
|
450
|
+
expect(thrownFailure((await call(WRITE, [offline])).outcome)).toMatchObject({
|
|
422
451
|
status: 0,
|
|
423
452
|
timedOut: false,
|
|
424
453
|
});
|
|
@@ -476,6 +505,6 @@ describe("send: the caller's signal", () => {
|
|
|
476
505
|
throw new DOMException("The operation timed out.", "TimeoutError");
|
|
477
506
|
};
|
|
478
507
|
const { outcome } = await call(WRITE, [timeout], { opts: { signal: controller.signal } });
|
|
479
|
-
expect(
|
|
508
|
+
expect(thrownFailure(outcome)).toMatchObject({ status: 0, timedOut: true });
|
|
480
509
|
});
|
|
481
510
|
});
|
package/src/transport.ts
CHANGED
|
@@ -78,6 +78,8 @@ export interface Failure {
|
|
|
78
78
|
retryAfterSecs: number | undefined;
|
|
79
79
|
/** The `x-request-id` header, for the API's support to find the call. */
|
|
80
80
|
requestId: string | undefined;
|
|
81
|
+
/** The answer's headers, for one your API adds to a refusal, such as what the call cost. */
|
|
82
|
+
headers: Headers | undefined;
|
|
81
83
|
/** The key the call was sent with. Retry with it, and a call that ran answers from its first run. */
|
|
82
84
|
idempotencyKey: string | undefined;
|
|
83
85
|
/** What fetch threw, when no answer came back. */
|
|
@@ -279,6 +281,7 @@ async function once<T, M>(
|
|
|
279
281
|
text: undefined,
|
|
280
282
|
retryAfterSecs: undefined,
|
|
281
283
|
requestId: undefined,
|
|
284
|
+
headers: undefined,
|
|
282
285
|
cause,
|
|
283
286
|
},
|
|
284
287
|
};
|
|
@@ -289,20 +292,36 @@ async function once<T, M>(
|
|
|
289
292
|
// The one place the SDK takes the API's word for a type: the contract says what `data` is.
|
|
290
293
|
return { ok: true, answer: { data: body["data"] as T, meta: body["meta"] as M | undefined } };
|
|
291
294
|
}
|
|
292
|
-
|
|
295
|
+
// ponytail: parses the body a second time, on a failure only, so a stream's refusal and a
|
|
296
|
+
// call's are read by one function.
|
|
297
|
+
return { ok: false, failure: failureOf(base, response, text) };
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* The failure an answer describes, for a request the SDK sent itself, such as a stream it opens
|
|
302
|
+
* with its own `fetch`. Read the body first and pass its text: a body can be read only once.
|
|
303
|
+
* `send` reads its own answers through this too, so both come out the same.
|
|
304
|
+
*/
|
|
305
|
+
export function failureOf(
|
|
306
|
+
call: Pick<Failure, "method" | "path" | "timeoutMs">,
|
|
307
|
+
response: Response,
|
|
308
|
+
text: string,
|
|
309
|
+
): Failure {
|
|
310
|
+
const error = envelopeError(parse(text)?.["error"]);
|
|
293
311
|
return {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
312
|
+
method: call.method,
|
|
313
|
+
path: call.path,
|
|
314
|
+
timeoutMs: call.timeoutMs,
|
|
315
|
+
// A 2xx lands here only when its body is not JSON, which is not the API answering.
|
|
316
|
+
status: response.status,
|
|
317
|
+
timedOut: false,
|
|
318
|
+
error,
|
|
319
|
+
text: error === undefined && text !== "" ? text.slice(0, 500) : undefined,
|
|
320
|
+
retryAfterSecs: parseRetryAfter(response.headers.get("retry-after")),
|
|
321
|
+
requestId: response.headers.get("x-request-id") ?? undefined,
|
|
322
|
+
headers: response.headers,
|
|
323
|
+
idempotencyKey: undefined,
|
|
324
|
+
cause: undefined,
|
|
306
325
|
};
|
|
307
326
|
}
|
|
308
327
|
|