@rest-vir/define-service 1.3.4 → 1.5.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.
@@ -104,7 +104,7 @@ export declare const endpointInitShape: Shape<{
104
104
  }> | import("@sinclair/typebox").TUnsafe<RegExp> | import("@sinclair/typebox").TString | import("@sinclair/typebox").TUndefined | import("@sinclair/typebox").TUnsafe<() => void> | import("@sinclair/typebox").TArray<import("@sinclair/typebox").TUnion<(import("@sinclair/typebox").TUnsafe<{
105
105
  anyOrigin: boolean;
106
106
  }> | import("@sinclair/typebox").TUnsafe<RegExp> | import("@sinclair/typebox").TString | import("@sinclair/typebox").TUnsafe<() => void>)[]>>)[]>>>;
107
- methods: import("@sinclair/typebox").TUnsafe<Partial<Record<HttpMethod, boolean>>>;
107
+ methods: Shape<import("@sinclair/typebox").TUnsafe<Partial<Record<HttpMethod, boolean>>>>;
108
108
  bypassResponseValidation: Shape<import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<(import("@sinclair/typebox").TBoolean | import("@sinclair/typebox").TNull | import("@sinclair/typebox").TUndefined)[]>>>;
109
109
  customProps: Shape<import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<(import("@sinclair/typebox").TUndefined | import("@sinclair/typebox").TUnsafe<Record<never, unknown>>)[]>>>;
110
110
  }>;
@@ -18,6 +18,8 @@ export type GenericFetchEndpointParams = PartialWithUndefined<GenericPathParams>
18
18
  bypassResponseValidation?: undefined | boolean;
19
19
  method?: HttpMethod | undefined;
20
20
  options?: Omit<RequestInit, 'body' | 'method'> | undefined;
21
+ /** If `true`, Skip automatic request data `'Content-Type'` header assignment. */
22
+ skipAutomaticContentType?: boolean | undefined;
21
23
  /**
22
24
  * A custom fetch implementation. Useful for debugging or unit testing. This can safely be
23
25
  * omitted to use the default JavaScript built-in global `fetch` function.
@@ -32,6 +34,15 @@ export type GenericFetchEndpointParams = PartialWithUndefined<GenericPathParams>
32
34
  * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
33
35
  */
34
36
  export type FetchMethod<EndpointToFetch extends Pick<EndpointDefinition, 'methods'>> = IsEqual<KeyCount<Record<ExtractKeysWithMatchingValues<EndpointToFetch['methods'], true>, boolean>>, 1> extends true ? never : Extract<HttpMethod, ExtractKeysWithMatchingValues<EndpointToFetch['methods'], true>> | `${Extract<HttpMethod, ExtractKeysWithMatchingValues<EndpointToFetch['methods'], true>>}`;
37
+ /**
38
+ * Properties from {@link GenericFetchEndpointParams} that are also used in
39
+ * {@link FetchEndpointParams}.
40
+ *
41
+ * @category Internal
42
+ * @category Package : @rest-vir/define-service
43
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
44
+ */
45
+ export type GenericFetchEndpointKeysAllowedInSpecificParams = 'options' | 'bypassResponseValidation' | 'skipAutomaticContentType';
35
46
  /**
36
47
  * All type safe parameters for sending a request to an endpoint using {@link fetchEndpoint}.
37
48
  *
@@ -64,7 +75,7 @@ export type FetchEndpointParams<EndpointToFetch extends SelectFrom<EndpointDefin
64
75
  method?: never;
65
76
  } : {
66
77
  method: FetchMethod<EndpointToFetch>;
67
- }) & (AllowFetchMock extends true ? Pick<GenericFetchEndpointParams, 'options' | 'fetch' | 'bypassResponseValidation'> : Pick<GenericFetchEndpointParams, 'options' | 'bypassResponseValidation'>)> : GenericFetchEndpointParams;
78
+ }) & (AllowFetchMock extends true ? Pick<GenericFetchEndpointParams, 'fetch' | GenericFetchEndpointKeysAllowedInSpecificParams> : Pick<GenericFetchEndpointParams, GenericFetchEndpointKeysAllowedInSpecificParams>)> : GenericFetchEndpointParams;
68
79
  /**
69
80
  * Type safe output from sending a request to an endpoint definition. Used by {@link fetchEndpoint}.
70
81
  *
@@ -84,7 +95,10 @@ export type FetchEndpointOutput<EndpointToFetch extends Readonly<SelectFrom<Endp
84
95
  response: Readonly<Response>;
85
96
  }> | Readonly<{
86
97
  ok: false;
87
- data: string | undefined;
98
+ data: EndpointToFetch extends SelectFrom<EndpointDefinition, {
99
+ requestDataShape: true;
100
+ responseDataShape: true;
101
+ }> ? EndpointExecutorData<EndpointToFetch>['response'] | string | undefined : any;
88
102
  response: Readonly<Response>;
89
103
  }>;
90
104
  /**
@@ -172,7 +186,7 @@ export declare function buildEndpointRequestInit<const EndpointToFetch extends R
172
186
  serviceOrigin: true;
173
187
  serviceName: true;
174
188
  };
175
- }>, ...[{ method, options, pathParams, requestData, searchParams, wildcard },]: CollapsedFetchEndpointParams<EndpointToFetch, false>): {
189
+ }>, ...[{ method, options, pathParams, requestData, searchParams, wildcard, skipAutomaticContentType, },]: CollapsedFetchEndpointParams<EndpointToFetch, false>): {
176
190
  url: string;
177
191
  requestInit: RequestInit;
178
192
  };
@@ -1,6 +1,6 @@
1
1
  import { check } from '@augment-vir/assert';
2
2
  import { addPrefix, filterMap, getObjectTypedEntries, HttpMethod, mapObject, } from '@augment-vir/common';
3
- import { assertValidShape } from 'object-shape-tester';
3
+ import { assertValidShape, checkWrapValidShape } from 'object-shape-tester';
4
4
  import { buildUrl } from 'url-vir';
5
5
  import { parseJsonWithUndefined } from '../augments/json.js';
6
6
  function defaultFetch(...[url, requestInit,]) {
@@ -64,10 +64,11 @@ export async function fetchEndpoint(endpoint, ...params) {
64
64
  const { requestInit, url } = buildEndpointRequestInit(endpoint, ...params);
65
65
  /* node:coverage ignore next: all tests mock fetch so we're never going to have a fallback here. */
66
66
  const response = await (fetch || defaultFetch)(url, requestInit, endpoint);
67
+ const responseText = await response.clone().text();
68
+ const responseData = endpoint.responseDataShape
69
+ ? parseJsonWithUndefined(responseText)
70
+ : undefined;
67
71
  if (response.ok) {
68
- const responseData = endpoint.responseDataShape
69
- ? parseJsonWithUndefined(await response.text())
70
- : undefined;
71
72
  if (endpoint.responseDataShape && !bypassResponseValidation) {
72
73
  assertValidShape(responseData, endpoint.responseDataShape, { allowExtraKeys: true });
73
74
  }
@@ -78,10 +79,16 @@ export async function fetchEndpoint(endpoint, ...params) {
78
79
  };
79
80
  }
80
81
  else {
82
+ const validResponseData = endpoint.responseDataShape
83
+ ? bypassResponseValidation
84
+ ? responseData
85
+ : checkWrapValidShape(responseData, endpoint.responseDataShape, {
86
+ allowExtraKeys: true,
87
+ })
88
+ : undefined;
81
89
  return {
82
90
  ok: false,
83
- /** This will be an error message. */
84
- data: (await response.text()) || undefined,
91
+ data: validResponseData || responseText || undefined,
85
92
  response,
86
93
  };
87
94
  }
@@ -93,7 +100,7 @@ export async function fetchEndpoint(endpoint, ...params) {
93
100
  * @category Package : @rest-vir/define-service
94
101
  * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
95
102
  */
96
- export function buildEndpointRequestInit(endpoint, ...[{ method, options = {}, pathParams, requestData, searchParams, wildcard } = {},]) {
103
+ export function buildEndpointRequestInit(endpoint, ...[{ method, options = {}, pathParams, requestData, searchParams, wildcard, skipAutomaticContentType, } = {},]) {
97
104
  const headers = mapObject(options.headers instanceof Headers
98
105
  ? Object.fromEntries(options.headers.entries())
99
106
  : check.isArray(options.headers)
@@ -105,11 +112,11 @@ export function buildEndpointRequestInit(endpoint, ...[{ method, options = {}, p
105
112
  };
106
113
  });
107
114
  if (!headers['content-type']) {
108
- if (requestData instanceof FormData) {
115
+ if (requestData instanceof FormData || skipAutomaticContentType) {
109
116
  /**
110
- * Do not set `content-type` manually when submitting form data because the browser will
111
- * set it automatically _and_ include a boundary in the content type, which is needed
112
- * for reading the form data properly.
117
+ * Do not set `content-type` manually when submitting form data because `fetch` will set
118
+ * it automatically _and_ include a boundary in the content type, which is needed for
119
+ * reading the form data properly.
113
120
  */
114
121
  }
115
122
  else if (requestData) {
@@ -9,7 +9,7 @@ import { type OriginRequirement } from '../util/origin.js';
9
9
  * @category Package : @rest-vir/define-service
10
10
  * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
11
11
  */
12
- export type MinimalService<ServiceName extends string = string> = {
12
+ export type MinimalService<ServiceName extends string = any> = {
13
13
  serviceName: IsEqual<ServiceName, ''> extends true ? never : ServiceName;
14
14
  /**
15
15
  * The origin at which the service will be hosted. Fetch requests and WebSocket connections will
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rest-vir/define-service",
3
- "version": "1.3.4",
3
+ "version": "1.5.0",
4
4
  "description": "Define an connect to a declarative and type safe REST and WebSocket service.",
5
5
  "keywords": [
6
6
  "rest",
@@ -40,14 +40,14 @@
40
40
  "test:update": "npm test update"
41
41
  },
42
42
  "dependencies": {
43
- "@augment-vir/assert": "^31.51.1",
44
- "@augment-vir/common": "^31.51.1",
45
- "date-vir": "^8.0.0",
46
- "type-fest": "^5.2.0",
47
- "url-vir": "^2.1.6"
43
+ "@augment-vir/assert": "^31.59.1",
44
+ "@augment-vir/common": "^31.59.1",
45
+ "date-vir": "^8.1.0",
46
+ "type-fest": "^5.4.3",
47
+ "url-vir": "^2.1.7"
48
48
  },
49
49
  "devDependencies": {
50
- "@augment-vir/test": "^31.51.1",
50
+ "@augment-vir/test": "^31.59.1",
51
51
  "@web/dev-server-esbuild": "^1.0.4",
52
52
  "@web/test-runner": "^0.20.2",
53
53
  "@web/test-runner-commands": "^0.9.0",
@@ -55,9 +55,9 @@
55
55
  "@web/test-runner-visual-regression": "^0.10.0",
56
56
  "istanbul-smart-text-reporter": "^1.1.5",
57
57
  "markdown-code-example-inserter": "^3.0.3",
58
- "object-shape-tester": "^6.9.3",
59
- "typedoc": "^0.28.14",
60
- "ws": "^8.18.3"
58
+ "object-shape-tester": "^6.11.0",
59
+ "typedoc": "^0.28.16",
60
+ "ws": "^8.19.0"
61
61
  },
62
62
  "peerDependencies": {
63
63
  "object-shape-tester": ">=5"