@autometa/http 1.4.19 → 2.0.0-rc.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.
Files changed (55) hide show
  1. package/README.md +107 -2
  2. package/dist/assertions/http-adapters.d.ts +35 -0
  3. package/dist/assertions/http-assertions-plugin.d.ts +16 -0
  4. package/dist/assertions/http-ensure.d.ts +42 -0
  5. package/dist/axios-transport.d.ts +22 -0
  6. package/dist/default-schema.d.ts +8 -0
  7. package/dist/fetch-transport.d.ts +21 -0
  8. package/dist/http-request.d.ts +109 -0
  9. package/dist/http-response.d.ts +77 -0
  10. package/dist/http.d.ts +300 -0
  11. package/dist/index.cjs +2076 -0
  12. package/dist/index.cjs.map +1 -0
  13. package/dist/index.d.ts +15 -1116
  14. package/dist/index.js +1727 -845
  15. package/dist/index.js.map +1 -1
  16. package/dist/plugins.d.ts +43 -0
  17. package/dist/request-meta.config.d.ts +56 -0
  18. package/dist/schema.map.d.ts +11 -0
  19. package/dist/transform-response.d.ts +1 -0
  20. package/dist/transport.d.ts +11 -0
  21. package/dist/types.d.ts +39 -0
  22. package/package.json +31 -31
  23. package/.eslintignore +0 -3
  24. package/.eslintrc.cjs +0 -4
  25. package/.turbo/turbo-lint$colon$fix.log +0 -4
  26. package/.turbo/turbo-prettify.log +0 -5
  27. package/.turbo/turbo-test.log +0 -120
  28. package/CHANGELOG.md +0 -326
  29. package/dist/esm/index.js +0 -1117
  30. package/dist/esm/index.js.map +0 -1
  31. package/dist/index.d.cts +0 -1116
  32. package/src/axios-client.ts +0 -35
  33. package/src/default-client-factory.axios.spec.ts +0 -9
  34. package/src/default-client-factory.other.spec.ts +0 -13
  35. package/src/default-client-factory.ts +0 -7
  36. package/src/default-schema.spec.ts +0 -74
  37. package/src/default-schema.ts +0 -127
  38. package/src/http-client.ts +0 -20
  39. package/src/http-request.spec.ts +0 -172
  40. package/src/http-request.ts +0 -201
  41. package/src/http-response.ts +0 -107
  42. package/src/http.spec.ts +0 -189
  43. package/src/http.ts +0 -907
  44. package/src/index.ts +0 -13
  45. package/src/node_modules/.vitest/deps/_metadata.json +0 -8
  46. package/src/node_modules/.vitest/deps/package.json +0 -3
  47. package/src/node_modules/.vitest/results.json +0 -1
  48. package/src/request-meta.config.spec.ts +0 -81
  49. package/src/request-meta.config.ts +0 -134
  50. package/src/request.config.ts +0 -34
  51. package/src/schema.map.spec.ts +0 -50
  52. package/src/schema.map.ts +0 -68
  53. package/src/transform-response.ts +0 -43
  54. package/src/types.ts +0 -37
  55. package/tsup.config.ts +0 -14
package/src/http.ts DELETED
@@ -1,907 +0,0 @@
1
- import { Fixture, INJECTION_SCOPE } from "@autometa/injection";
2
- import { AxiosClient } from "./axios-client";
3
- import { HTTPClient } from "./http-client";
4
- import { defaultClientFactory } from "./default-client-factory";
5
- import { HTTPRequest, HTTPRequestBuilder } from "./http-request";
6
- import { HTTPResponse } from "./http-response";
7
- import { MetaConfig, MetaConfigBuilder } from "./request-meta.config";
8
- import {
9
- HTTPAdditionalOptions,
10
- RequestHook,
11
- ResponseHook,
12
- SchemaParser,
13
- StatusCode,
14
- } from "./types";
15
- import { transformResponse } from "./transform-response";
16
- import { AutomationError } from "@autometa/errors";
17
-
18
- /**
19
- * The HTTP fixture allows requests to be built and sent to a server. In general,
20
- * there are 2 modes of operation:
21
- *
22
- * * Shared Chain: The shared chain is used to configure the client for all requests, such as
23
- * routes this instance will always be used. When a shared chain method is called, it returns
24
- * the same instance of HTTP which can be further chained to configure the client.
25
- * * Request Chain: The request chain is used to configure a single request, inheriting values
26
- * set by the shared chain. When called, a new HTTP client instance is created and inherits the values
27
- * set by it's parent.
28
- *
29
- * The 2 modes are intended to simplify configuring an object through an inheritance chain. For example,
30
- * assume we have an API with 2 controller routes, `/product` and `/seller`. We can set up a Base Client
31
- * which consumes the HTTP fixture and configures it with the base url of our API.
32
- *
33
- * Inheritors can further configure their HTTP instance's routes.
34
- *
35
- * ```ts
36
- * \@Constructor(HTTP)
37
- * export class BaseClient {
38
- * constructor(protected readonly http: HTTP) {
39
- * this.http.url("https://api.example.com");
40
- * }
41
- * }
42
- *
43
- * export class ProductClient extends BaseClient {
44
- * constructor(http: HTTP) {
45
- * super(http);
46
- * this.http.sharedRoute("product");
47
- * }
48
- * getProduct(id: number) {
49
- * return this.http.route(id).get();
50
- * }
51
- *
52
- * export class SellerClient extends BaseClient {
53
- * constructor(http: HTTP) {
54
- * super(http);
55
- * this.http.sharedRoute("seller");
56
- * }
57
- *
58
- * getSeller(id: number) {
59
- * return this.http.route(id).get();
60
- * }
61
- * }
62
- * ```
63
- *
64
- * 'Schemas' can also be configured. A Schema is a function or an object with a `parse` method, which
65
- * takes a response data payload and returns a validated object. Schemas are mapped to
66
- * HTTP Status Codes, and if configured to be required the request will fail if no schema is found
67
- * matching that code.
68
- *
69
- * Defining a schema function:
70
- *
71
- * ```
72
- * // user.schema.ts
73
- * export function UserSchema(data: unknown) {
74
- * if(typeof data !== "object") {
75
- * throw new Error("Expected an object");
76
- * }
77
- *
78
- * if(typeof data.name !== "string") {
79
- * throw new Error("Expected a string");
80
- * }
81
- *
82
- * return data as { name: string };
83
- * }
84
- *
85
- * // user.controller.ts
86
- * \@Fixture(INJECTION_SCOPE.TRANSIENT)
87
- * export class UserController extends BaseController {
88
- * constructor(private readonly http: HTTP) {
89
- * super(http);
90
- * this.http
91
- * .sharedRoute("user")
92
- * .sharedSchema(ErrorSchema, { from: 400, to: 499 });
93
- * }
94
- *
95
- * getUser(id: number) {
96
- * return this.http.route(id).schema(UserSchema, 200).get();
97
- * // or
98
- * return this.http
99
- * .route(id)
100
- * .schema(UserSchema, { from: 200, to: 299 })
101
- * .get();
102
- * // or
103
- * return this.http
104
- * .route(id)
105
- * .schema(UserSchema, 200, 201, 202)
106
- * .get();
107
- * }
108
- * }
109
- * ```
110
- *
111
- * Validation libraries which use a `.parse` or `.validation`, method, such as Zod or MyZod, can also be used as schemas:
112
- *
113
- * ```ts
114
- * // user.schema.ts
115
- * import { z } from "myzod";
116
- *
117
- * export const UserSchema = z.object({
118
- * name: z.string()
119
- * });
120
- *
121
- * // user.controller.ts
122
- * \@Fixture(INJECTION_SCOPE.TRANSIENT)
123
- * export class UserController extends BaseController {
124
- * constructor(private readonly http: HTTP) {
125
- * super(http);
126
- * this.http
127
- * .sharedRoute("user")
128
- * .sharedSchema(ErrorSchema, { from: 400, to: 499 })
129
- * }
130
- *
131
- * getUser(id: number) {
132
- * return this.http.route(id).schema(UserSchema, 200).get();
133
- * }
134
- * }
135
- * ```
136
- */
137
- @Fixture(INJECTION_SCOPE.TRANSIENT)
138
- export class HTTP {
139
- #request: HTTPRequestBuilder<HTTPRequest<unknown>>;
140
- #metaConfig: MetaConfigBuilder;
141
- constructor(
142
- private readonly client: HTTPClient = defaultClientFactory(),
143
- builder: HTTPRequestBuilder<
144
- HTTPRequest<unknown>
145
- > = new HTTPRequestBuilder(),
146
- metaConfig: MetaConfigBuilder = new MetaConfigBuilder()
147
- ) {
148
- this.#request = builder;
149
- this.#metaConfig = metaConfig.derive();
150
- }
151
-
152
- static create(
153
- client: HTTPClient = new AxiosClient(),
154
- builder: HTTPRequestBuilder<
155
- HTTPRequest<unknown>
156
- > = new HTTPRequestBuilder(),
157
- metaConfig: MetaConfigBuilder = new MetaConfigBuilder()
158
- ) {
159
- const derived = new HTTP(client, builder, metaConfig);
160
- return derived;
161
- }
162
-
163
- /**
164
- * Sets the base url of the request for this client, such as
165
- * `https://api.example.com`, and could include always-used routes like
166
- * the api version, such as `/v1` or `/api/v1` at the end.
167
- *
168
- * ```ts
169
- *
170
- * \@Fixture(INJECTION_SCOPE.TRANSIENT)
171
- * export abstract class BaseClient {
172
- * constructor(protected readonly http: HTTP) {
173
- * this.http.url("https://api.example.com");
174
- * }
175
- * }
176
- * ```
177
- * @param url
178
- * @returns
179
- */
180
- url(url: string) {
181
- this.#request.url(url);
182
- return this;
183
- }
184
-
185
- sharedOptions(options: HTTPAdditionalOptions<unknown>) {
186
- this.#metaConfig.options(options);
187
- return this;
188
- }
189
-
190
- /**
191
- * If set to true, all requests derived from this client will require a schema be defined
192
- * matching any response status code. If set to false, a schema will still be used for validation
193
- * if defined, or the unadulterated original body will be returned if no schema matches.
194
- *
195
- * @param required Whether or not a schema is required for all responses.
196
- * @returns This instance of HTTP.
197
- */
198
- requireSchema(required: boolean) {
199
- this.#metaConfig.requireSchema(required);
200
- return this;
201
- }
202
-
203
- /**
204
- * If set to true, all requests derived from this client will allow plain text
205
- * responses. If set to false, plain text responses will throw an serialization error.
206
- *
207
- * Useful when an endpoint returns a HTML or plain text response. If the plain text
208
- * is the value of `true` or `false`, or a number, it will be parsed into the
209
- * appropriate type.
210
- *
211
- * This method is a shared chain method, and will return the same instance of HTTP.
212
- *
213
- * @param allow Whether or not plain text responses are allowed.
214
- * @returns This instance of HTTP.
215
- */
216
- sharedAllowPlainText(allow: boolean) {
217
- this.#metaConfig.allowPlainText(allow);
218
- return this;
219
- }
220
-
221
- /**
222
- * If set to true, all requests derived from this client will allow plain text
223
- * responses. If set to false, plain text responses will throw an serialization error.
224
- *
225
- * Useful when an endpoint returns a HTML or plain text response. If the plain text
226
- * is the value of `true` or `false`, or a number, it will be parsed into the
227
- * appropriate type.
228
- *
229
- * This method is a request chain method, and will return a new instance of HTTP.
230
- *
231
- * @param allow Whether or not plain text responses are allowed.
232
- * @returns A new child instance of HTTP derived from this one.
233
- */
234
- allowPlainText(allow: boolean) {
235
- return HTTP.create(
236
- this.client,
237
- this.#request.derive(),
238
- this.#metaConfig.derive().allowPlainText(allow)
239
- );
240
- }
241
-
242
- /**
243
- * Attaches a route to the request, such as `/product` or `/user`. Subsequent calls
244
- * to this method will append the route to the existing route, such as `/product/1`.
245
- *
246
- * Numbers will be converted to strings automatically. Routes can be defined one
247
- * at a time or as a spread argument.
248
- *
249
- * ```ts
250
- * constructor(http: HTTP) {
251
- * super(http);
252
- * this.http.sharedRoute("user", id).get();
253
- * }
254
- *
255
- * // or
256
- *
257
- * constructor(http: HTTP) {
258
- * super(http);
259
- * this.http
260
- * .sharedRoute("user")
261
- * .sharedRoute(id)
262
- * .get();
263
- * }
264
- * ```
265
- *
266
- * This method is a shared chain method, and will return the same instance of HTTP. All
267
- * child clients will inherit the routes defined by this method. Useful to configure
268
- * in the constructor body.
269
- *
270
- * @param route A route or spread list of routes to append to the request.
271
- * @returns This instance of HTTP.
272
- */
273
- sharedRoute(...route: (string | number | boolean)[]) {
274
- this.#request.route(...route.map((r) => r.toString()));
275
- return this;
276
- }
277
-
278
- /**
279
- * Attaches a route to the request, such as `/product` or `/user`. Subsequent calls
280
- * to this method will append the route to the existing route, such as `/product/1`.
281
- *
282
- * Numbers will be converted to strings automatically. Routes can be defined one
283
- * at a time or as a spread argument.
284
- *
285
- * ```ts
286
- * getUser(id: number) {
287
- * return this.http.route("user", id).get();
288
- * }
289
- *
290
- * // or
291
- *
292
- * getUser(id: number) {
293
- * return this.http
294
- * .route("user")
295
- * .route(id)
296
- * .get();
297
- * }
298
- * ```
299
- *
300
- * This method is a request chain method, and will return a new instance of HTTP, inheriting
301
- * any routes previously defined and appending the new route. Useful to configure
302
- * in class methods as part of finalizing a request.
303
- *
304
- * @param route A route or spread list of routes to append to the request.
305
- * @returns A new child instance of HTTP derived from this one.
306
- */
307
- route(...route: (string | number | boolean)[]) {
308
- const mapped = route.map((r) => String(r));
309
- return HTTP.create(
310
- this.client,
311
- this.#request.derive().route(...mapped),
312
- this.#metaConfig.derive()
313
- );
314
- }
315
-
316
- /**
317
- * Attaches a shared schema mapping for all requests by this client. Schemas are
318
- * mapped to HTTP Status Codes, and if configured to be required the request will fail
319
- * if no schema is found matching that code.
320
- *
321
- * The status code mapping can be defined as a single code, a range of codes, or a spread list.
322
- *
323
- * ```ts
324
- * \@Fixture(INJECTION_SCOPE.TRANSIENT)
325
- * export class UserController extends BaseController {
326
- * constructor(private readonly http: HTTP) {
327
- * super(http);
328
- * this.http
329
- * .sharedRoute("user")
330
- * .sharedSchema(UserSchema, 200)
331
- * .sharedSchema(EmptySchema, 201, 204)
332
- * .sharedSchema(ErrorSchema, { from: 400, to: 499 });
333
- * }
334
- * }
335
- * ```
336
- *
337
- * This method is a shared chain method, and will return the same instance of HTTP. All
338
- * child clients will inherit the schemas defined by this method. Useful to configure
339
- * in the constructor body.
340
- *
341
- * @param parser The schema parser to use for this mapping.
342
- * @param codes A single status code, a range of status codes, or a spread list of status codes.
343
- * @returns This instance of HTTP.
344
- */
345
- sharedSchema(parser: SchemaParser, ...codes: StatusCode[]): HTTP;
346
- sharedSchema(
347
- parser: SchemaParser,
348
- ...range: { from: StatusCode; to: StatusCode }[]
349
- ): HTTP;
350
- sharedSchema(
351
- parser: SchemaParser,
352
- ...args: (StatusCode | { from: StatusCode; to: StatusCode })[]
353
- ): HTTP {
354
- this.#metaConfig.schema(parser, ...args);
355
- return this;
356
- }
357
-
358
- /**
359
- * Attaches a schema mapping for this request. Schemas are
360
- * mapped to HTTP Status Codes, and if configured to be required the request will fail
361
- * if no schema is found matching that code.
362
- *
363
- * The status code mapping can be defined as a single code, a range of codes, or a spread list.
364
- *
365
- * ```ts
366
- * \@Fixture(INJECTION_SCOPE.TRANSIENT)
367
- * export class UserController extends BaseController {
368
- * constructor(private readonly http: HTTP) {
369
- * super(http);
370
- * this.http
371
- * .sharedRoute("user")
372
- * .schema(ErrorSchema, { from: 400, to: 499 });
373
- * }
374
- *
375
- * getUser(id: number) {
376
- * return this.http.route(id).schema(UserSchema, 200).get();
377
- * }
378
- *
379
- * getUsers(...ids: number[]) {
380
- * return this.http
381
- * .route("users")
382
- * .schema(UserSchema, { from: 200, to: 299 })
383
- * .schema(UserSchema, 200)
384
- * .get();
385
- * }
386
- * ```
387
- *
388
- * This method is a request chain method, and will return a new instance of HTTP, inheriting
389
- * any schemas previously defined and appending the new schema. Useful to configure
390
- * in class methods as part of finalizing a request.
391
- *
392
- * @param parser The schema parser to use for this mapping.
393
- * @param codes A single status code, a range of status codes, or a spread list of status codes.
394
- * @returns A new child instance of HTTP derived from this one.
395
- */
396
- schema(parser: SchemaParser, ...codes: StatusCode[]): HTTP;
397
- schema(
398
- parser: SchemaParser,
399
- ...range: { from: StatusCode; to: StatusCode }[]
400
- ): HTTP;
401
- schema(
402
- parser: SchemaParser,
403
- ...args: (StatusCode | { from: StatusCode; to: StatusCode })[]
404
- ): HTTP {
405
- return HTTP.create(
406
- this.client,
407
- this.#request.derive(),
408
- this.#metaConfig.derive().schema(parser, ...args)
409
- );
410
- }
411
-
412
- /**
413
- * Attaches a shared query string parameter to all requests by this client. Query string
414
- * parameters are key-value pairs which are appended to the request url, such as
415
- * `https://api.example.com?name=John&age=30`.
416
- *
417
- * This method is a shared chain method, and will return the same instance of HTTP. All
418
- * child clients will inherit the query string parameters defined by this method. Useful to configure
419
- * in the constructor body.
420
- *
421
- * @param name The name of the query string parameter.
422
- * @param value The value of the query string parameter.
423
- * @returns This instance of HTTP.
424
- */
425
- sharedParam(name: string, value: Record<string, unknown>): HTTP;
426
- sharedParam(name: string, ...value: (string | number | boolean)[]): HTTP;
427
- sharedParam(name: string, value: (string | number | boolean)[]): HTTP;
428
- sharedParam(
429
- name: string,
430
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
431
- value: any
432
- ): HTTP {
433
- this.#request.param(name, value);
434
- return this;
435
- }
436
-
437
- /**
438
- * `onSend` is a pre-request hook which will be executed in order of definition
439
- * immediately before the request is sent. This hook can be used to analyze or
440
- * log the request state.
441
- *
442
- * ```ts
443
- *
444
- * \@Fixture(INJECTION_SCOPE.TRANSIENT)
445
- * export class UserController extends BaseController {
446
- * constructor(private readonly http: HTTP) {
447
- * super(http);
448
- * this.http
449
- * .sharedRoute("user")
450
- * .sharedOnSend("log request",
451
- * (request) => console.log(JSON.stringify(request, null, 2))
452
- * );
453
- * }
454
- * }
455
- * ```
456
- *
457
- * This method is a shared chain method, and will return the same instance of HTTP. All
458
- * child clients will inherit the onSend hooks defined by this method. Useful to configure
459
- * in the constructor body.
460
- *
461
- * @param description A description of the hook, used for debugging.
462
- * @param hook The hook to execute.
463
- * @returns This instance of HTTP.
464
- */
465
- sharedOnSend(description: string, hook: RequestHook) {
466
- this.#metaConfig.onBeforeSend(description, hook);
467
- return this;
468
- }
469
-
470
- /**
471
- * `onReceive` is a post-request hook which will be executed in order of definition
472
- * immediately after the response is received. This hook can be used to analyze or
473
- * log the response state.
474
- *
475
- * ```ts
476
- *
477
- * \@Fixture(INJECTION_SCOPE.TRANSIENT)
478
- * export class UserController extends BaseController {
479
- * constructor(private readonly http: HTTP) {
480
- * super(http);
481
- * this.http
482
- * .sharedRoute("user")
483
- * .sharedOnReceive("log response",
484
- * (response) => console.log(JSON.stringify(response, null, 2))
485
- * );
486
- * }
487
- * }
488
- * ```
489
- *
490
- * This method is a shared chain method, and will return the same instance of HTTP. All
491
- * child clients will inherit the onReceive hooks defined by this method. Useful to configure
492
- * in the constructor body.
493
- *
494
- * @param description A description of the hook, used for debugging.
495
- * @param hook The hook to execute.
496
- * @returns This instance of HTTP.
497
- */
498
- sharedOnReceive(description: string, hook: ResponseHook<unknown>) {
499
- this.#metaConfig.onReceiveResponse(description, hook);
500
- return this;
501
- }
502
-
503
- /**
504
- * Attaches a query string parameter object to the request. Query string
505
- * parameters are key-value pairs which are appended to the request url, such as
506
- * `https://api.example.com?name=John&age=30`.
507
- *
508
- * This method is a shared chain method, and will return the same instance of HTTP. All
509
- * child clients will inherit the query string parameters defined by this method. Useful to configure
510
- * in the constructor body.
511
- *
512
- * ```ts
513
- * constructor(http: HTTP) {
514
- * super(http);
515
- * this.http
516
- * .sharedParams({ 'is-test': "true" })
517
- * ```
518
- * @param name The name of the query string parameter.
519
- * @param value The value of the query string parameter.
520
- * @returns This instance of HTTP.
521
- */
522
- sharedParams(dict: Record<string, unknown>) {
523
- this.#request.params(dict);
524
- return this;
525
- }
526
-
527
- /**
528
- * Attaches a query string parameter to the request. Query string
529
- * parameters are key-value pairs which are appended to the request url, such as
530
- * `https://api.example.com?name=John&age=30`.
531
- *
532
- * This method is a request chain method, and will return a new instance of HTTP, inheriting
533
- * any query string parameters previously defined and appending the new parameter. Useful to configure
534
- * in class methods as part of finalizing a request.
535
- *
536
- * ```ts
537
- * getUser(id: number) {
538
- * return this.http
539
- * .route(id)
540
- * .param("name", "John")
541
- * .param("age", 30)
542
- * ```
543
- *
544
- * Note: Numbers and Booleans will be converted to strings automatically.
545
- *
546
- * @param name The name of the query string parameter.
547
- * @param value The value of the query string parameter.
548
- * @returns A new child instance of HTTP derived from this one.
549
- */
550
- param(name: string, value: Record<string, unknown>): HTTP;
551
- param(name: string, ...value: (string | number | boolean)[]): HTTP;
552
- param(name: string, value: (string | number | boolean)[]): HTTP;
553
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
554
- param(name: string, ...value: any) {
555
- return HTTP.create(
556
- this.client,
557
- this.#request.derive().param(name, value),
558
- this.#metaConfig.derive()
559
- );
560
- }
561
-
562
- /**
563
- * Attaches a query string parameter object to the request. Query string
564
- * parameters are key-value pairs which are appended to the request url, such as
565
- * `https://api.example.com?name=John&age=30`.
566
- *
567
- * This method is a shared chain method, and will return the same instance of HTTP. All
568
- * child clients will inherit the query string parameters defined by this method. Useful to configure
569
- * in the constructor body.
570
- *
571
- * ```ts
572
- * getUser(id: number) {
573
- * return this.http
574
- * .route(id)
575
- * .param({ name: "John", age: "30" })
576
- *
577
- * @param name The name of the query string parameter.
578
- * @param value The value of the query string parameter.
579
- * @returns This instance of HTTP.
580
- */
581
- params(dict: Record<string, unknown>) {
582
- return HTTP.create(
583
- this.client,
584
- this.#request.derive().params(dict),
585
- this.#metaConfig.derive()
586
- );
587
- }
588
-
589
- /**
590
- * Attaches a shared data payload to this client. The data payload is the body of the request,
591
- * and can be any type. If the data payload is an object, it will be serialized to JSON.
592
- *
593
- * This method is a shared chain method, and will return the same instance of HTTP. All
594
- * child clients will inherit the data payload defined by this method. Useful to configure
595
- * in the constructor body.
596
- *
597
- * @param data The data payload to attach to the request.
598
- * @returns This instance of HTTP.
599
- */
600
- sharedData<T>(data: T) {
601
- this.#request.data(data);
602
- return this;
603
- }
604
-
605
- /**
606
- * Attaches a shared header to this client. Headers are string:string key-value pairs which are
607
- * sent with the request, such as `Content-Type: application/json`.
608
- *
609
- * Numbers, Booleans and Null will be converted to string values automatically.
610
- *
611
- * A Factory function can also be provided to generate the header value at the time of request.
612
- *
613
- * This method is a shared chain method, and will return the same instance of HTTP. All
614
- * child clients will inherit the header defined by this method. Useful to configure
615
- * in the constructor body.
616
- *
617
- * @param name The name of the header.
618
- * @param value The value of the header.
619
- */
620
- sharedHeader(
621
- name: string,
622
- value:
623
- | string
624
- | number
625
- | boolean
626
- | null
627
- | (string | number | boolean)[]
628
- | (() => string | number | boolean | null)
629
- | (() => Promise<string | number | boolean | null>)
630
- ) {
631
- this.#request.header(name, value);
632
- return this;
633
- }
634
-
635
- header(
636
- name: string,
637
- value:
638
- | string
639
- | number
640
- | boolean
641
- | null
642
- | (string | number | boolean)[]
643
- | (() => string | number | boolean | null)
644
- | (() => Promise<string | number | boolean | null>)
645
- ) {
646
- return HTTP.create(
647
- this.client,
648
- this.#request.derive().header(name, value),
649
- this.#metaConfig.derive()
650
- );
651
- }
652
-
653
- /**
654
- * Attaches a data payload to this request. The data payload is the body of the request,
655
- * and can be any type. If the data payload is an object, it will be serialized to JSON.
656
- *
657
- * This method is a request chain method, and will return a new instance of HTTP, inheriting
658
- * any data payload previously defined and appending the new payload. Useful to configure
659
- * in class methods as part of finalizing a request.
660
- *
661
- * @param data The data payload to attach to the request.
662
- * @returns A new child instance of HTTP derived from this one.
663
- */
664
- data<T>(data: T) {
665
- this.#request.data(data);
666
- return HTTP.create(
667
- this.client,
668
- this.#request.derive().data(data),
669
- this.#metaConfig.derive()
670
- );
671
- }
672
-
673
- /**
674
- * `onSend` is a pre-request hook which will be executed in order of definition
675
- * immediately before the request is sent. This hook can be used to modify the request,
676
- * or to log the state of a request before final send-off.
677
- *
678
- * ```ts
679
- *
680
- * \@Fixture(INJECTION_SCOPE.TRANSIENT)
681
- * export class UserController extends BaseController {
682
- * constructor(private readonly http: HTTP) {
683
- * super(http);
684
- * }
685
- *
686
- * getUser(id: number) {
687
- * return this.http
688
- * .route(id)
689
- * .onSend("log request",
690
- * (request) => console.log(JSON.stringify(request, null, 2)
691
- * )
692
- * .get();
693
- * }
694
- * ```
695
- *
696
- * This method is a request chain method, and will return a new instance of HTTP, inheriting
697
- * any onSend hooks previously defined and appending the new hook. Useful to configure
698
- * in class methods as part of finalizing a request.
699
- *
700
- * @param description A description of the hook, used for debugging.
701
- * @param hook The hook to execute.
702
- * @returns A new child instance of HTTP derived from this one.
703
- */
704
- onSend(description: string, hook: RequestHook) {
705
- return HTTP.create(
706
- this.client,
707
- this.#request.derive(),
708
- this.#metaConfig.derive().onBeforeSend(description, hook)
709
- );
710
- }
711
-
712
- /**
713
- * `onReceive` is a post-request hook which will be executed in order of definition
714
- * immediately after the response is received. This hook can be used to modify the response,
715
- * or to log the state of a response after it is received.
716
- *
717
- * ```ts
718
- *
719
- * \@Fixture(INJECTION_SCOPE.TRANSIENT)
720
- * export class UserController extends BaseController {
721
- * constructor(private readonly http: HTTP) {
722
- * super(http);
723
- * }
724
- *
725
- * getUser(id: number) {
726
- * return this.http
727
- * .route(id)
728
- * .onReceive("log response",
729
- * (response) => console.log(JSON.stringify(response, null, 2)
730
- * )
731
- * .get();
732
- * }
733
- * ```
734
- *
735
- * This method is a request chain method, and will return a new instance of HTTP, inheriting
736
- * any onReceive hooks previously defined and appending the new hook. Useful to configure
737
- * in class methods as part of finalizing a request.
738
- *
739
- * @param description A description of the hook, used for debugging.
740
- * @param hook The hook to execute.
741
- * @returns A new child instance of HTTP derived from this one.
742
- */
743
- onReceive(description: string, hook: ResponseHook<unknown>) {
744
- return HTTP.create(
745
- this.client,
746
- this.#request.derive(),
747
- this.#metaConfig.derive().onReceiveResponse(description, hook)
748
- );
749
- }
750
-
751
- /**
752
- * Executes the current request state as a GET request.
753
- *
754
- * @param options Additional options to pass to the underlying http client, such
755
- * as e.g Axios configuration values.
756
- * @returns A promise which resolves to the response.
757
- */
758
- get<TResponseType>(options?: HTTPAdditionalOptions<unknown>) {
759
- return this.#makeRequest(
760
- this.#request.derive().method("GET"),
761
- options
762
- ) as Promise<HTTPResponse<TResponseType>>;
763
- }
764
-
765
- /**
766
- * Executes the current request state as a POST request.
767
- *
768
- * @param data The data payload to attach to the request.
769
- * @param options Additional options to pass to the underlying http client, such
770
- * as e.g Axios configuration values.
771
- * @returns A promise which resolves to the response.
772
- */
773
- post<TResponseType>(options?: HTTPAdditionalOptions<unknown>) {
774
- return this.#makeRequest(
775
- this.#request.derive().method("POST"),
776
- options
777
- ) as Promise<HTTPResponse<TResponseType>>;
778
- }
779
-
780
- /**
781
- * Executes the current request state as a DELETE request.
782
- *
783
- * @param options Additional options to pass to the underlying http client, such
784
- * as e.g Axios configuration values.
785
- * @returns A promise which resolves to the response.
786
- * as e.g Axios configuration values.
787
- */
788
- delete<TResponseType>(options?: HTTPAdditionalOptions<unknown>) {
789
- return this.#makeRequest(
790
- this.#request.derive().method("DELETE"),
791
- options
792
- ) as Promise<HTTPResponse<TResponseType>>;
793
- }
794
-
795
- /**
796
- * Executes the current request state as a PUT request.
797
- *
798
- * @param options Additional options to pass to the underlying http client, such
799
- * as e.g Axios configuration values.
800
- * @returns A promise which resolves to the response.
801
- */
802
- put<TResponseType>(options?: HTTPAdditionalOptions<unknown>) {
803
- return this.#makeRequest(
804
- this.#request.derive().method("PUT"),
805
- options
806
- ) as Promise<HTTPResponse<TResponseType>>;
807
- }
808
- /**
809
- * Executes the current request state as a PATCH request.
810
- *
811
- * @param options Additional options to pass to the underlying http client, such
812
- * as e.g Axios configuration values.
813
- * @returns A promise which resolves to the response.
814
- */
815
- patch<TResponseType>(options?: HTTPAdditionalOptions<unknown>) {
816
- return this.#makeRequest(
817
- this.#request.derive().method("PATCH"),
818
- options
819
- ) as Promise<HTTPResponse<TResponseType>>;
820
- }
821
-
822
- head<TResponseType>(options?: HTTPAdditionalOptions<unknown>) {
823
- return this.#makeRequest(
824
- this.#request.derive().method("HEAD"),
825
- options
826
- ) as Promise<HTTPResponse<TResponseType>>;
827
- }
828
-
829
- options<TResponseType>(options?: HTTPAdditionalOptions<unknown>) {
830
- return this.#makeRequest(
831
- this.#request.derive().method("OPTIONS"),
832
- options
833
- ) as Promise<HTTPResponse<TResponseType>>;
834
- }
835
-
836
- trace<TResponseType>(options?: HTTPAdditionalOptions<unknown>) {
837
- return this.#makeRequest(
838
- this.#request.derive().method("TRACE"),
839
- options
840
- ) as Promise<HTTPResponse<TResponseType>>;
841
- }
842
-
843
- connect<TResponseType>(options?: HTTPAdditionalOptions<unknown>) {
844
- return this.#makeRequest(
845
- this.#request.derive().method("CONNECT"),
846
- options
847
- ) as Promise<HTTPResponse<TResponseType>>;
848
- }
849
-
850
- async #makeRequest(
851
- builder: HTTPRequestBuilder<HTTPRequest<unknown>>,
852
- options?: HTTPAdditionalOptions<unknown>
853
- ) {
854
- const request = (await builder.resolveDynamicHeaders()).build();
855
- const meta = this.#metaConfig.derive().build();
856
- await this.runOnSendHooks(meta, request);
857
- const opts = { ...meta.options, ...options };
858
- const result = await this.client.request<unknown, string>(request, opts);
859
- result.data = transformResponse(meta.allowPlainText, result.data);
860
- await this.runOnReceiveHooks(meta, result);
861
- const validated = this.#validateResponse(result, meta);
862
- return validated;
863
- }
864
-
865
- private async runOnSendHooks(
866
- meta: MetaConfig,
867
- request: HTTPRequest<unknown>
868
- ) {
869
- for (const [description, hook] of meta.onSend) {
870
- try {
871
- await hook(request);
872
- } catch (e) {
873
- const cause = e as Error;
874
- const msg = `An error occurred while sending a request in hook: '${description}'`;
875
- throw new AutomationError(msg, { cause });
876
- }
877
- }
878
- }
879
- private async runOnReceiveHooks(
880
- meta: MetaConfig,
881
- response: HTTPResponse<unknown>
882
- ) {
883
- for (const [description, hook] of meta.onReceive) {
884
- try {
885
- await hook(response);
886
- } catch (e) {
887
- const cause = e as Error;
888
- const msg = `An error occurred while receiving a response in hook: '${description}'`;
889
- throw new AutomationError(msg, { cause });
890
- }
891
- }
892
- }
893
-
894
- #validateResponse<T>(
895
- response: HTTPResponse<unknown>,
896
- meta: MetaConfig
897
- ): HTTPResponse<T> {
898
- const { status, data } = response;
899
- const validated = meta.schemas.validate(
900
- status,
901
- data,
902
- meta.requireSchema
903
- ) as T;
904
- response.data = validated;
905
- return response as HTTPResponse<T>;
906
- }
907
- }