@rest-vir/define-service 1.6.0 → 1.7.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.
|
@@ -160,6 +160,67 @@ export declare function fetchEndpoint<const EndpointToFetch extends Readonly<Sel
|
|
|
160
160
|
serviceName: true;
|
|
161
161
|
};
|
|
162
162
|
}>, ...params: CollapsedFetchEndpointParams<EndpointToFetch>): Promise<FetchEndpointOutput<EndpointToFetch>>;
|
|
163
|
+
/**
|
|
164
|
+
* Type safe output from sending a stream request to an endpoint definition. Used by
|
|
165
|
+
* {@link fetchStreamEndpoint}.
|
|
166
|
+
*
|
|
167
|
+
* @category Internal
|
|
168
|
+
* @category Package : @rest-vir/define-service
|
|
169
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
170
|
+
*/
|
|
171
|
+
export type FetchStreamEndpointOutput = Readonly<{
|
|
172
|
+
ok: true;
|
|
173
|
+
stream: ReadableStream<Uint8Array>;
|
|
174
|
+
response: Readonly<Response>;
|
|
175
|
+
}> | Readonly<{
|
|
176
|
+
ok: false;
|
|
177
|
+
data: string | undefined;
|
|
178
|
+
response: Readonly<Response>;
|
|
179
|
+
}>;
|
|
180
|
+
/**
|
|
181
|
+
* Send a request to an endpoint definition and return a `ReadableStream` instead of parsing the
|
|
182
|
+
* response body. This is useful for consuming SSE (Server-Sent Events) endpoints from frontend
|
|
183
|
+
* clients.
|
|
184
|
+
*
|
|
185
|
+
* Uses the same request-building and validation flow as {@link fetchEndpoint}, but skips response
|
|
186
|
+
* body and JSON validation.
|
|
187
|
+
*
|
|
188
|
+
* @category Client (Frontend) Connection
|
|
189
|
+
* @category Package : @rest-vir/define-service
|
|
190
|
+
* @example
|
|
191
|
+
*
|
|
192
|
+
* ```ts
|
|
193
|
+
* import {fetchStreamEndpoint} from '@rest-vir/define-service';
|
|
194
|
+
*
|
|
195
|
+
* const result = await fetchStreamEndpoint(myService.endpoints['/my-sse-endpoint']);
|
|
196
|
+
*
|
|
197
|
+
* if (result.ok) {
|
|
198
|
+
* const reader = result.stream.getReader();
|
|
199
|
+
* }
|
|
200
|
+
* ```
|
|
201
|
+
*
|
|
202
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
203
|
+
*/
|
|
204
|
+
export declare function fetchStreamEndpoint<const EndpointToFetch extends Readonly<SelectFrom<EndpointDefinition, {
|
|
205
|
+
requestDataShape: true;
|
|
206
|
+
path: true;
|
|
207
|
+
responseDataShape: true;
|
|
208
|
+
methods: true;
|
|
209
|
+
service: {
|
|
210
|
+
serviceOrigin: true;
|
|
211
|
+
serviceName: true;
|
|
212
|
+
};
|
|
213
|
+
}>> | NoParam>(endpoint: EndpointToFetch extends EndpointDefinition ? EndpointToFetch : SelectFrom<EndpointDefinition, {
|
|
214
|
+
requestDataShape: true;
|
|
215
|
+
path: true;
|
|
216
|
+
responseDataShape: true;
|
|
217
|
+
searchParamsShape: true;
|
|
218
|
+
methods: true;
|
|
219
|
+
service: {
|
|
220
|
+
serviceOrigin: true;
|
|
221
|
+
serviceName: true;
|
|
222
|
+
};
|
|
223
|
+
}>, ...params: CollapsedFetchEndpointParams<EndpointToFetch>): Promise<FetchStreamEndpointOutput>;
|
|
163
224
|
/**
|
|
164
225
|
* Build request init and URL for fetching an endpoint. Used in {@link fetchEndpoint}.
|
|
165
226
|
*
|
|
@@ -97,6 +97,64 @@ export async function fetchEndpoint(endpoint, ...params) {
|
|
|
97
97
|
};
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Send a request to an endpoint definition and return a `ReadableStream` instead of parsing the
|
|
102
|
+
* response body. This is useful for consuming SSE (Server-Sent Events) endpoints from frontend
|
|
103
|
+
* clients.
|
|
104
|
+
*
|
|
105
|
+
* Uses the same request-building and validation flow as {@link fetchEndpoint}, but skips response
|
|
106
|
+
* body and JSON validation.
|
|
107
|
+
*
|
|
108
|
+
* @category Client (Frontend) Connection
|
|
109
|
+
* @category Package : @rest-vir/define-service
|
|
110
|
+
* @example
|
|
111
|
+
*
|
|
112
|
+
* ```ts
|
|
113
|
+
* import {fetchStreamEndpoint} from '@rest-vir/define-service';
|
|
114
|
+
*
|
|
115
|
+
* const result = await fetchStreamEndpoint(myService.endpoints['/my-sse-endpoint']);
|
|
116
|
+
*
|
|
117
|
+
* if (result.ok) {
|
|
118
|
+
* const reader = result.stream.getReader();
|
|
119
|
+
* }
|
|
120
|
+
* ```
|
|
121
|
+
*
|
|
122
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
123
|
+
*/
|
|
124
|
+
export async function fetchStreamEndpoint(endpoint, ...params) {
|
|
125
|
+
const { requestData, fetch } = params[0] || {};
|
|
126
|
+
if (requestData) {
|
|
127
|
+
if (endpoint.requestDataShape) {
|
|
128
|
+
assertValidShape(requestData, endpoint.requestDataShape, {
|
|
129
|
+
allowExtraKeys: true,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
throw new Error(`Request data was given but endpoint '${endpoint.path}' is not expecting any request data.`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const { requestInit, url } = buildEndpointRequestInit(endpoint, ...params);
|
|
137
|
+
/* node:coverage ignore next: all tests mock fetch so we're never going to have a fallback here. */
|
|
138
|
+
const response = await (fetch || defaultFetch)(url, requestInit, endpoint);
|
|
139
|
+
if (response.ok) {
|
|
140
|
+
if (!response.body) {
|
|
141
|
+
throw new Error(`Endpoint '${endpoint.path}' returned an ok response with no body to stream.`);
|
|
142
|
+
}
|
|
143
|
+
return {
|
|
144
|
+
ok: true,
|
|
145
|
+
stream: response.body,
|
|
146
|
+
response,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
const responseText = await response.clone().text();
|
|
151
|
+
return {
|
|
152
|
+
ok: false,
|
|
153
|
+
data: responseText || undefined,
|
|
154
|
+
response,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
}
|
|
100
158
|
/**
|
|
101
159
|
* Build request init and URL for fetching an endpoint. Used in {@link fetchEndpoint}.
|
|
102
160
|
*
|
|
@@ -5,7 +5,7 @@ import { type CommonWebSocket } from '../web-socket/common-web-socket.js';
|
|
|
5
5
|
import { type ClientWebSocket, type GenericConnectWebSocketParams } from '../web-socket/overwrite-web-socket-methods.js';
|
|
6
6
|
import { type GenericWebSocketDefinition } from '../web-socket/web-socket-definition.js';
|
|
7
7
|
import { type CollapsedConnectWebSocketParams } from './connect-web-socket.js';
|
|
8
|
-
import { buildEndpointUrl, type CollapsedFetchEndpointParams, type FetchEndpointOutput, type GenericFetchEndpointParams } from './fetch-endpoint.js';
|
|
8
|
+
import { buildEndpointUrl, type CollapsedFetchEndpointParams, type FetchEndpointOutput, type FetchStreamEndpointOutput, type GenericFetchEndpointParams } from './fetch-endpoint.js';
|
|
9
9
|
/**
|
|
10
10
|
* An API generated by {@link generateApi}. This can be easily mocked by wrapping this API in
|
|
11
11
|
* {@link makeMockApi}.
|
|
@@ -23,6 +23,12 @@ export declare class RestVirApi<SpecificService extends ServiceDefinition> {
|
|
|
23
23
|
[EndpointPath in keyof SpecificService['endpoints']]: SpecificService['endpoints'][EndpointPath] extends GenericEndpointDefinition ? SpecificService['endpoints'][EndpointPath] & {
|
|
24
24
|
/** Send a fetch request to this endpoint. */
|
|
25
25
|
fetch(...params: CollapsedFetchEndpointParams<SpecificService['endpoints'][EndpointPath]>): Promise<FetchEndpointOutput<SpecificService['endpoints'][EndpointPath]>>;
|
|
26
|
+
/**
|
|
27
|
+
* Send a fetch request to this endpoint and return a `ReadableStream` instead of
|
|
28
|
+
* parsing the response body. Useful for consuming SSE (Server-Sent Events)
|
|
29
|
+
* endpoints.
|
|
30
|
+
*/
|
|
31
|
+
fetchStream(...params: CollapsedFetchEndpointParams<SpecificService['endpoints'][EndpointPath]>): Promise<FetchStreamEndpointOutput>;
|
|
26
32
|
buildUrl(params: Parameters<typeof buildEndpointUrl<SpecificService['endpoints'][EndpointPath]>>[1]): string;
|
|
27
33
|
} : never;
|
|
28
34
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mapObjectValues, } from '@augment-vir/common';
|
|
2
2
|
import { connectWebSocket } from './connect-web-socket.js';
|
|
3
|
-
import { buildEndpointUrl, fetchEndpoint, } from './fetch-endpoint.js';
|
|
3
|
+
import { buildEndpointUrl, fetchEndpoint, fetchStreamEndpoint, } from './fetch-endpoint.js';
|
|
4
4
|
/**
|
|
5
5
|
* An API generated by {@link generateApi}. This can be easily mocked by wrapping this API in
|
|
6
6
|
* {@link makeMockApi}.
|
|
@@ -32,16 +32,23 @@ export class RestVirApi {
|
|
|
32
32
|
constructor(service, { endpointFetch, webSocketConnect, serviceOrigin } = {}) {
|
|
33
33
|
this.serviceOrigin = serviceOrigin || service.serviceOrigin;
|
|
34
34
|
this.endpoints = mapObjectValues(service.endpoints, (endpointPath, endpointDefinition) => {
|
|
35
|
+
const endpointWithOrigin = () => ({
|
|
36
|
+
...endpointDefinition,
|
|
37
|
+
service: {
|
|
38
|
+
...endpointDefinition.service,
|
|
39
|
+
serviceOrigin: this.serviceOrigin,
|
|
40
|
+
},
|
|
41
|
+
});
|
|
35
42
|
return {
|
|
36
43
|
...endpointDefinition,
|
|
37
44
|
fetch: (...params) => {
|
|
38
|
-
return fetchEndpoint({
|
|
39
|
-
...
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
+
return fetchEndpoint(endpointWithOrigin(), {
|
|
46
|
+
...endpointFetch,
|
|
47
|
+
...params[0],
|
|
48
|
+
});
|
|
49
|
+
},
|
|
50
|
+
fetchStream: (...params) => {
|
|
51
|
+
return fetchStreamEndpoint(endpointWithOrigin(), {
|
|
45
52
|
...endpointFetch,
|
|
46
53
|
...params[0],
|
|
47
54
|
});
|
|
@@ -119,6 +126,12 @@ export function makeMockApi(api, mocks) {
|
|
|
119
126
|
...params[0],
|
|
120
127
|
});
|
|
121
128
|
},
|
|
129
|
+
fetchStream: (...params) => {
|
|
130
|
+
return fetchStreamEndpoint(endpointDefinition, {
|
|
131
|
+
fetch: mocks.fetch,
|
|
132
|
+
...params[0],
|
|
133
|
+
});
|
|
134
|
+
},
|
|
122
135
|
};
|
|
123
136
|
}),
|
|
124
137
|
webSockets: mapObjectValues(api.webSockets, (webSocketPath, webSocketDefinition) => {
|