@rest-vir/define-service 0.0.2

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 (50) hide show
  1. package/LICENSE-CC0 +121 -0
  2. package/LICENSE-MIT +21 -0
  3. package/README.md +88 -0
  4. package/dist/augments/json.d.ts +8 -0
  5. package/dist/augments/json.js +13 -0
  6. package/dist/dev-port/find-dev-port.d.ts +132 -0
  7. package/dist/dev-port/find-dev-port.js +156 -0
  8. package/dist/endpoint/endpoint-path.d.ts +27 -0
  9. package/dist/endpoint/endpoint-path.js +14 -0
  10. package/dist/endpoint/endpoint.d.ts +198 -0
  11. package/dist/endpoint/endpoint.js +80 -0
  12. package/dist/frontend-connect/connect-web-socket.d.ts +95 -0
  13. package/dist/frontend-connect/connect-web-socket.js +64 -0
  14. package/dist/frontend-connect/fetch-endpoint.d.ts +199 -0
  15. package/dist/frontend-connect/fetch-endpoint.js +135 -0
  16. package/dist/frontend-connect/generate-api.d.ts +102 -0
  17. package/dist/frontend-connect/generate-api.js +83 -0
  18. package/dist/frontend-connect/mock-client-web-socket.d.ts +187 -0
  19. package/dist/frontend-connect/mock-client-web-socket.js +198 -0
  20. package/dist/frontend-connect/web-socket-protocol-parse.d.ts +10 -0
  21. package/dist/frontend-connect/web-socket-protocol-parse.js +111 -0
  22. package/dist/index.d.ts +23 -0
  23. package/dist/index.js +23 -0
  24. package/dist/service/define-service.d.ts +19 -0
  25. package/dist/service/define-service.js +133 -0
  26. package/dist/service/match-url.d.ts +30 -0
  27. package/dist/service/match-url.js +31 -0
  28. package/dist/service/minimal-service.d.ts +44 -0
  29. package/dist/service/minimal-service.js +1 -0
  30. package/dist/service/service-definition.d.ts +80 -0
  31. package/dist/service/service-definition.error.d.ts +35 -0
  32. package/dist/service/service-definition.error.js +34 -0
  33. package/dist/service/service-definition.js +1 -0
  34. package/dist/util/mock-fetch.d.ts +107 -0
  35. package/dist/util/mock-fetch.js +199 -0
  36. package/dist/util/no-param.d.ts +16 -0
  37. package/dist/util/no-param.js +8 -0
  38. package/dist/util/origin.d.ts +43 -0
  39. package/dist/util/origin.js +19 -0
  40. package/dist/util/path-to-regexp.d.ts +54 -0
  41. package/dist/util/path-to-regexp.js +307 -0
  42. package/dist/util/search-params.d.ts +9 -0
  43. package/dist/util/search-params.js +1 -0
  44. package/dist/web-socket/common-web-socket.d.ts +103 -0
  45. package/dist/web-socket/common-web-socket.js +28 -0
  46. package/dist/web-socket/overwrite-web-socket-methods.d.ts +276 -0
  47. package/dist/web-socket/overwrite-web-socket-methods.js +210 -0
  48. package/dist/web-socket/web-socket-definition.d.ts +170 -0
  49. package/dist/web-socket/web-socket-definition.js +78 -0
  50. package/package.json +68 -0
@@ -0,0 +1,198 @@
1
+ import { AnyObject, HttpMethod, JsonCompatibleValue, Overwrite, type SelectFrom } from '@augment-vir/common';
2
+ import { type ShapeDefinition, type ShapeToRuntimeType } from 'object-shape-tester';
3
+ import { type RequireAtLeastOne } from 'type-fest';
4
+ import { type MinimalService } from '../service/minimal-service.js';
5
+ import { type NoParam } from '../util/no-param.js';
6
+ import { type OriginRequirement } from '../util/origin.js';
7
+ import { BaseSearchParams } from '../util/search-params.js';
8
+ import { type EndpointPathBase } from './endpoint-path.js';
9
+ /**
10
+ * Base Endpoint request/response shape type.
11
+ *
12
+ * @category Internal
13
+ * @category Package : @rest-vir/define-service
14
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
15
+ */
16
+ export type EndpointDataShapeBase = JsonCompatibleValue;
17
+ /**
18
+ * The type for setting up an individual endpoint, used in `defineService`.
19
+ *
20
+ * @category Internal
21
+ * @category Package : @rest-vir/define-service
22
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
23
+ */
24
+ export type EndpointInit<AllowedMethods extends RequireAtLeastOne<Record<HttpMethod, boolean>> = RequireAtLeastOne<Record<HttpMethod, boolean>>, RequestDataShape extends EndpointDataShapeBase | NoParam = EndpointDataShapeBase | NoParam, ResponseDataShape = unknown> = {
25
+ /**
26
+ * Shape definition for request data. Set to `undefined` for no request data.
27
+ *
28
+ * See the [`object-shape-tester`](https://www.npmjs.com/package/object-shape-tester) package
29
+ * for extra details on defining a shape.
30
+ */
31
+ requestDataShape: RequestDataShape;
32
+ /**
33
+ * Shape definition for response data. Set to `undefined` for no response data.
34
+ *
35
+ * See the [`object-shape-tester`](https://www.npmjs.com/package/object-shape-tester) package
36
+ * for extra details on defining a shape.
37
+ */
38
+ responseDataShape: ResponseDataShape;
39
+ /**
40
+ * All allowed (or not allowed) HTTP methods for this endpoint. Set `true` for each allowed
41
+ * method and omit or set `false` for each blocked method.
42
+ */
43
+ methods: AllowedMethods;
44
+ /**
45
+ * Set a required client origin for this endpoint.
46
+ *
47
+ * - If this is omitted, the service's origin requirement is used instead.
48
+ * - If this is explicitly set to `undefined`, this endpoint allows any origins (regardless of the
49
+ * service's origin requirement).
50
+ * - Any other set value overrides the service's origin requirement (if it has any).
51
+ */
52
+ requiredClientOrigin?: OriginRequirement;
53
+ /**
54
+ * A shape used to verify search params. This should match the entire search params object.
55
+ *
56
+ * Note the following:
57
+ *
58
+ * - Search param values will _always_ be in an array
59
+ * - Elements in search param value arrays will _always_ be strings
60
+ *
61
+ * @example
62
+ *
63
+ * ```ts
64
+ * import {exact, enumShape} from 'object-shape-tester';
65
+ *
66
+ * const partialEndpointInit = {
67
+ * searchParamsShape: {
68
+ * // use `tupleShape` to ensure there's exactly one entry for this search param
69
+ * userId: tupleShape(enumShape(MyEnum)),
70
+ * date: tupleShape(exact('2')),
71
+ * // don't use `tupleShape` here so that there can be any number of entries
72
+ * colors: [''],
73
+ * },
74
+ * };
75
+ * ```
76
+ */
77
+ searchParamsShape?: unknown;
78
+ /**
79
+ * Any additional data that you wish to attach to this endpoint. This won't be used by the
80
+ * service at all, it's merely a place for you to place extra data which will be passed along to
81
+ * your endpoint implementations.
82
+ */
83
+ customProps?: Record<PropertyKey, unknown> | undefined;
84
+ };
85
+ /**
86
+ * Used to validate {@link EndpointInit} inside of `defineService`.
87
+ *
88
+ * @category Internal
89
+ * @category Package : @rest-vir/define-service
90
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
91
+ */
92
+ export declare const endpointInitShape: ShapeDefinition<{
93
+ requestDataShape: import("object-shape-tester").ShapeUnknown<[unknown]>;
94
+ responseDataShape: import("object-shape-tester").ShapeUnknown<[unknown]>;
95
+ searchParamsShape: import("object-shape-tester").ShapeUnknown<[unknown]>;
96
+ /**
97
+ * Set a required client origin for this endpoint.
98
+ *
99
+ * - If this is omitted, the service's origin requirement is used instead.
100
+ * - If this is explicitly set to `undefined`, this endpoint allows any origins (regardless of the
101
+ * service's origin requirement).
102
+ * - Any other set value overrides the service's origin requirement (if it has any).
103
+ */
104
+ requiredClientOrigin: import("object-shape-tester").ShapeOr<[undefined, string, import("object-shape-tester").ShapeClass<[RegExpConstructor]>, () => void, import("object-shape-tester").ShapeOr<[string, import("object-shape-tester").ShapeClass<[RegExpConstructor]>, () => void]>[]]>;
105
+ methods: import("object-shape-tester").ShapeIndexedKeys<[{
106
+ keys: import("object-shape-tester").ShapeEnum<readonly [typeof HttpMethod]>;
107
+ values: boolean;
108
+ required: false;
109
+ }]>;
110
+ customProps: import("object-shape-tester").ShapeOptional<import("object-shape-tester").ShapeOr<[undefined, import("object-shape-tester").ShapeIndexedKeys<[{
111
+ keys: import("object-shape-tester").ShapeUnknown<[unknown]>;
112
+ values: import("object-shape-tester").ShapeUnknown<[unknown]>;
113
+ required: false;
114
+ }]>]>>;
115
+ }, false>;
116
+ /**
117
+ * Adds final properties to {@link EndpointInit} so it becomes {@link EndpointDefinition}.
118
+ *
119
+ * @category Internal
120
+ * @category Package : @rest-vir/define-service
121
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
122
+ */
123
+ export type WithFinalEndpointProps<Init, EndpointPath extends EndpointPathBase> = (Init extends AnyObject ? Overwrite<Init, {
124
+ requestDataShape: Init['requestDataShape'] extends NoParam ? ShapeDefinition<any, true> | undefined : undefined extends Init['requestDataShape'] ? undefined : ShapeDefinition<Init['requestDataShape'], true>;
125
+ responseDataShape: Init['responseDataShape'] extends NoParam ? ShapeDefinition<any, true> | undefined : undefined extends Init['responseDataShape'] ? undefined : ShapeDefinition<Init['responseDataShape'], true>;
126
+ RequestType: Init['requestDataShape'] extends NoParam ? any : undefined extends Init['requestDataShape'] ? undefined : ShapeToRuntimeType<ShapeDefinition<Init['requestDataShape'], true>, false, true>;
127
+ ResponseType: Init['responseDataShape'] extends NoParam ? any : undefined extends Init['responseDataShape'] ? undefined : ShapeToRuntimeType<ShapeDefinition<Init['responseDataShape'], true>, false, true>;
128
+ searchParamsShape: 'searchParamsShape' extends keyof Init ? undefined extends Init['searchParamsShape'] ? undefined : ShapeDefinition<Init['searchParamsShape'], true> | undefined : undefined;
129
+ SearchParamsType: undefined extends Init['searchParamsShape'] ? BaseSearchParams : ShapeToRuntimeType<ShapeDefinition<Init['searchParamsShape'], true>, false, true>;
130
+ customProps: 'customProps' extends keyof Init ? Init['customProps'] : undefined;
131
+ }> : never) & {
132
+ path: EndpointPath;
133
+ isWebSocket: false;
134
+ isEndpoint: true;
135
+ service: MinimalService;
136
+ };
137
+ /**
138
+ * A fully defined endpoint instance. This is generated from `defineService`.
139
+ *
140
+ * @category Internal
141
+ * @category Package : @rest-vir/define-service
142
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
143
+ */
144
+ export type EndpointDefinition<AllowedMethods extends RequireAtLeastOne<Record<HttpMethod, boolean>> = RequireAtLeastOne<Record<HttpMethod, boolean>>, RequestDataShape extends EndpointDataShapeBase | NoParam = NoParam, ResponseDataShape extends EndpointDataShapeBase | NoParam = NoParam, EndpointPath extends EndpointPathBase = EndpointPathBase> = WithFinalEndpointProps<EndpointInit<AllowedMethods, RequestDataShape, ResponseDataShape>, EndpointPath>;
145
+ /**
146
+ * A generic version of {@link EndpointDefinition} that any endpoint can be assigned to.
147
+ *
148
+ * @category Internal
149
+ * @category Package : @rest-vir/define-service
150
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
151
+ */
152
+ export type GenericEndpointDefinition = Overwrite<EndpointDefinition, {
153
+ searchParamsShape: any;
154
+ SearchParamsType: any;
155
+ }>;
156
+ /**
157
+ * Extracts response and request data from an endpoint definition.
158
+ *
159
+ * @category Internal
160
+ * @category Package : @rest-vir/define-service
161
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
162
+ */
163
+ export type BaseEndpointForExecutorData = Pick<EndpointDefinition, 'requestDataShape' | 'responseDataShape'>;
164
+ /**
165
+ * Extracts response and request data from an endpoint definition into different properties.
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 EndpointExecutorData<SpecificEndpoint extends BaseEndpointForExecutorData> = {
172
+ request: ShapeToRuntimeType<SpecificEndpoint['requestDataShape'], false, true>;
173
+ response: ShapeToRuntimeType<SpecificEndpoint['responseDataShape'], false, true>;
174
+ };
175
+ /**
176
+ * Attaches request and response type-only getters to an endpoint definition.
177
+ *
178
+ * @category Internal
179
+ * @category Package : @rest-vir/define-service
180
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
181
+ */
182
+ export declare function attachEndpointShapeTypeGetters<const T extends AnyObject>(endpoint: T): asserts endpoint is T & Pick<EndpointDefinition, 'RequestType' | 'ResponseType'>;
183
+ /**
184
+ * Asserts that the given finalized {@link EndpointDefinition} instance is valid.
185
+ *
186
+ * @category Internal
187
+ * @category Package : @rest-vir/define-service
188
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
189
+ */
190
+ export declare function assertValidEndpoint(endpoint: Readonly<SelectFrom<EndpointDefinition, {
191
+ path: true;
192
+ methods: true;
193
+ isWebSocket: true;
194
+ isEndpoint: true;
195
+ service: {
196
+ serviceName: true;
197
+ };
198
+ }>>): void;
@@ -0,0 +1,80 @@
1
+ import { HttpMethod, } from '@augment-vir/common';
2
+ import { defineShape, enumShape, indexedKeys, optional, or, unknownShape, } from 'object-shape-tester';
3
+ import { ensureServiceDefinitionError } from '../service/service-definition.error.js';
4
+ import { originRequirementShape } from '../util/origin.js';
5
+ import { assertValidEndpointPath } from './endpoint-path.js';
6
+ /**
7
+ * Used to validate {@link EndpointInit} inside of `defineService`.
8
+ *
9
+ * @category Internal
10
+ * @category Package : @rest-vir/define-service
11
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
12
+ */
13
+ export const endpointInitShape = defineShape({
14
+ requestDataShape: unknownShape(),
15
+ responseDataShape: unknownShape(),
16
+ searchParamsShape: unknownShape(),
17
+ /**
18
+ * Set a required client origin for this endpoint.
19
+ *
20
+ * - If this is omitted, the service's origin requirement is used instead.
21
+ * - If this is explicitly set to `undefined`, this endpoint allows any origins (regardless of the
22
+ * service's origin requirement).
23
+ * - Any other set value overrides the service's origin requirement (if it has any).
24
+ */
25
+ requiredClientOrigin: originRequirementShape,
26
+ methods: indexedKeys({
27
+ keys: enumShape(HttpMethod),
28
+ values: false,
29
+ required: false,
30
+ }),
31
+ customProps: optional(or(undefined, indexedKeys({
32
+ keys: unknownShape(),
33
+ values: unknownShape(),
34
+ required: false,
35
+ }))),
36
+ });
37
+ /**
38
+ * Attaches request and response type-only getters to an endpoint definition.
39
+ *
40
+ * @category Internal
41
+ * @category Package : @rest-vir/define-service
42
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
43
+ */
44
+ export function attachEndpointShapeTypeGetters(endpoint) {
45
+ Object.defineProperties(endpoint, {
46
+ RequestType: {
47
+ enumerable: false,
48
+ get() {
49
+ throw new Error('.RequestType should not be used as a value.');
50
+ },
51
+ },
52
+ ResponseType: {
53
+ enumerable: false,
54
+ get() {
55
+ throw new Error('.ResponseType should not be used as a value.');
56
+ },
57
+ },
58
+ });
59
+ }
60
+ /**
61
+ * Asserts that the given finalized {@link EndpointDefinition} instance is valid.
62
+ *
63
+ * @category Internal
64
+ * @category Package : @rest-vir/define-service
65
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
66
+ */
67
+ export function assertValidEndpoint(endpoint) {
68
+ try {
69
+ assertValidEndpointPath(endpoint.path);
70
+ if (!Object.values(endpoint.methods).some((value) => value)) {
71
+ throw new Error('Endpoint has no allowed HTTP methods.');
72
+ }
73
+ }
74
+ catch (error) {
75
+ throw ensureServiceDefinitionError(error, {
76
+ serviceName: endpoint.service.serviceName,
77
+ ...endpoint,
78
+ });
79
+ }
80
+ }
@@ -0,0 +1,95 @@
1
+ import { RequiredKeysOf, SelectFrom } from '@augment-vir/common';
2
+ import { IsNever } from 'type-fest';
3
+ import { PathParams } from '../endpoint/endpoint-path.js';
4
+ import { type NoParam } from '../util/no-param.js';
5
+ import type { CommonWebSocket } from '../web-socket/common-web-socket.js';
6
+ import { type ClientWebSocket, type ConnectWebSocketListeners, type GenericConnectWebSocketParams } from '../web-socket/overwrite-web-socket-methods.js';
7
+ import { type WebSocketDefinition } from '../web-socket/web-socket-definition.js';
8
+ /**
9
+ * Verifies that no WebSocket protocols in the given list are invalid. This doesn't have anything to
10
+ * do with a `WebsocketDefinition` instance, it's just checking if the given protocols can even be
11
+ * sent.
12
+ *
13
+ * @category Internal
14
+ * @category Package : @rest-vir/define-service
15
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
16
+ */
17
+ export declare function assertValidWebSocketProtocols(protocols: ReadonlyArray<unknown> | undefined): void;
18
+ /**
19
+ * Builds a WebSocket URL.
20
+ *
21
+ * @category Internal
22
+ * @category Package : @rest-vir/define-service
23
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
24
+ */
25
+ export declare function buildWebSocketUrl(webSocketDefinition: Readonly<SelectFrom<WebSocketDefinition, {
26
+ path: true;
27
+ service: {
28
+ serviceName: true;
29
+ serviceOrigin: true;
30
+ };
31
+ }>>, ...[{ pathParams },]: CollapsedConnectWebSocketParams): string;
32
+ /**
33
+ * Params for {@link connectWebSocket}.
34
+ *
35
+ * @category Internal
36
+ * @category Package : @rest-vir/define-service
37
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
38
+ */
39
+ export type ConnectWebSocketParams<WebSocketToConnect extends Readonly<SelectFrom<WebSocketDefinition, {
40
+ path: true;
41
+ MessageFromClientType: true;
42
+ MessageFromHostType: true;
43
+ protocolsShape: true;
44
+ ProtocolsType: true;
45
+ searchParamsShape: true;
46
+ SearchParamsType: true;
47
+ }>>, AllowWebSocketMock extends boolean = true, WebSocketClass extends CommonWebSocket = CommonWebSocket> = {
48
+ /**
49
+ * Optional listeners that can be immediately attached to the WebSocket instance instead of
50
+ * requiring externally adding them.
51
+ */
52
+ listeners?: ConnectWebSocketListeners<WebSocketToConnect, WebSocketClass>;
53
+ } & (IsNever<PathParams<WebSocketToConnect['path']>> extends true ? {
54
+ /** This WebSocket has no path parameters to configure. */
55
+ pathParams?: undefined;
56
+ } : PathParams<WebSocketToConnect['path']> extends string ? {
57
+ /** Required path params for this WebSocket's path. */
58
+ pathParams: Readonly<Record<PathParams<WebSocketToConnect['path']>, string>>;
59
+ } : {
60
+ /** This WebSocket has no path parameters to configure. */
61
+ pathParams?: undefined;
62
+ }) & (AllowWebSocketMock extends true ? Pick<GenericConnectWebSocketParams<WebSocketClass>, 'webSocketConstructor'> : unknown) & (WebSocketToConnect['protocolsShape'] extends undefined ? {
63
+ protocols?: string[];
64
+ } : {
65
+ protocols: WebSocketToConnect['ProtocolsType'];
66
+ }) & (WebSocketToConnect['searchParamsShape'] extends undefined ? {
67
+ searchParams?: string[];
68
+ } : {
69
+ searchParams: WebSocketToConnect['SearchParamsType'];
70
+ });
71
+ /**
72
+ * Collapsed version of {@link ConnectWebSocketParams} for {@link connectWebSocket} that only
73
+ * _requires_ an object parameter if the parameters object has any required keys.
74
+ *
75
+ * @category Internal
76
+ * @category Package : @rest-vir/define-service
77
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
78
+ */
79
+ export type CollapsedConnectWebSocketParams<WebSocketToConnect extends Readonly<SelectFrom<WebSocketDefinition, {
80
+ path: true;
81
+ MessageFromClientType: true;
82
+ MessageFromHostType: true;
83
+ protocolsShape: true;
84
+ ProtocolsType: true;
85
+ searchParamsShape: true;
86
+ SearchParamsType: true;
87
+ }>> | NoParam = NoParam, AllowWebSocketMock extends boolean = true, WebSocketClass extends CommonWebSocket = CommonWebSocket> = WebSocketToConnect extends NoParam ? [Readonly<GenericConnectWebSocketParams<WebSocketClass>>?] : Readonly<ConnectWebSocketParams<Exclude<WebSocketToConnect, NoParam>, AllowWebSocketMock, WebSocketClass>> extends infer RealParams ? RequiredKeysOf<RealParams> extends never ? [RealParams?] : [RealParams] : [];
88
+ /**
89
+ * Creates and connects a new client WebSocket instance with type safety.
90
+ *
91
+ * @category Client (Frontend) Connection
92
+ * @category Package : @rest-vir/define-service
93
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
94
+ */
95
+ export declare function connectWebSocket<const WebSocketToConnect extends Readonly<WebSocketDefinition> | NoParam, WebSocketClass extends CommonWebSocket>(webSocketDefinition: WebSocketToConnect extends WebSocketDefinition ? WebSocketToConnect : Readonly<WebSocketDefinition>, ...params: CollapsedConnectWebSocketParams<WebSocketToConnect, true, WebSocketClass>): Promise<ClientWebSocket<WebSocketToConnect, WebSocketClass>>;
@@ -0,0 +1,64 @@
1
+ import { ensureErrorAndPrependMessage, HttpMethod, } from '@augment-vir/common';
2
+ import { buildUrl } from 'url-vir';
3
+ import { finalizeWebSocket, WebSocketLocation, } from '../web-socket/overwrite-web-socket-methods.js';
4
+ import { buildEndpointUrl } from './fetch-endpoint.js';
5
+ import { parseSecWebSocketProtocolHeader } from './web-socket-protocol-parse.js';
6
+ /**
7
+ * Verifies that no WebSocket protocols in the given list are invalid. This doesn't have anything to
8
+ * do with a `WebsocketDefinition` instance, it's just checking if the given protocols can even be
9
+ * sent.
10
+ *
11
+ * @category Internal
12
+ * @category Package : @rest-vir/define-service
13
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
14
+ */
15
+ export function assertValidWebSocketProtocols(protocols) {
16
+ if (!protocols || !protocols.length) {
17
+ return;
18
+ }
19
+ const joinedProtocols = protocols.join(', ');
20
+ try {
21
+ parseSecWebSocketProtocolHeader(joinedProtocols);
22
+ }
23
+ catch (error) {
24
+ throw ensureErrorAndPrependMessage(error, `Invalid protocols given ('${joinedProtocols}')`);
25
+ }
26
+ }
27
+ /**
28
+ * Builds a WebSocket URL.
29
+ *
30
+ * @category Internal
31
+ * @category Package : @rest-vir/define-service
32
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
33
+ */
34
+ export function buildWebSocketUrl(webSocketDefinition, ...[{ pathParams } = {},]) {
35
+ const httpUrl = buildEndpointUrl({
36
+ methods: {
37
+ [HttpMethod.Get]: true,
38
+ },
39
+ path: webSocketDefinition.path,
40
+ requestDataShape: undefined,
41
+ responseDataShape: undefined,
42
+ service: webSocketDefinition.service,
43
+ }, { pathParams });
44
+ return buildUrl(httpUrl, {
45
+ protocol: httpUrl.startsWith('https') ? 'wss' : 'ws',
46
+ }).href;
47
+ }
48
+ const defaultWebSocket = function (...[url, protocols,]) {
49
+ return new globalThis.WebSocket(url, protocols);
50
+ };
51
+ /**
52
+ * Creates and connects a new client WebSocket instance with type safety.
53
+ *
54
+ * @category Client (Frontend) Connection
55
+ * @category Package : @rest-vir/define-service
56
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
57
+ */
58
+ export async function connectWebSocket(webSocketDefinition, ...params) {
59
+ const [{ webSocketConstructor = defaultWebSocket, protocols, listeners } = {},] = params;
60
+ assertValidWebSocketProtocols(protocols);
61
+ const url = buildWebSocketUrl(webSocketDefinition, ...params);
62
+ const clientWebSocket = await finalizeWebSocket(webSocketDefinition, new webSocketConstructor(url, protocols, webSocketDefinition), listeners, WebSocketLocation.OnClient);
63
+ return clientWebSocket;
64
+ }
@@ -0,0 +1,199 @@
1
+ import { HttpMethod, KeyCount, type ExtractKeysWithMatchingValues, type MaybePromise, type RequiredKeysOf, type SelectFrom } from '@augment-vir/common';
2
+ import { type IsEqual, type IsNever } from 'type-fest';
3
+ import { type PathParams } from '../endpoint/endpoint-path.js';
4
+ import { EndpointExecutorData, type EndpointDefinition, type GenericEndpointDefinition } from '../endpoint/endpoint.js';
5
+ import { type NoParam } from '../util/no-param.js';
6
+ /**
7
+ * A general version of {@link FetchEndpointParams} to be used when accepting _any_ endpoint (like in
8
+ * tests).
9
+ *
10
+ * @category Internal
11
+ * @category Package : @rest-vir/define-service
12
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
13
+ */
14
+ export type GenericFetchEndpointParams = {
15
+ pathParams?: Record<string, string> | undefined;
16
+ requestData?: any;
17
+ method?: HttpMethod | undefined;
18
+ options?: Omit<RequestInit, 'body' | 'method'> | undefined;
19
+ /**
20
+ * A custom fetch implementation. Useful for debugging or unit testing. This can safely be
21
+ * omitted to use the default JavaScript built-in global `fetch` function.
22
+ */
23
+ fetch?: ((url: string, requestInit: RequestInit, endpoint?: GenericEndpointDefinition | undefined) => MaybePromise<Response>) | undefined;
24
+ };
25
+ /**
26
+ * Type that determines which HTTP request methods can be used for the given endpoint definition.
27
+ *
28
+ * @category Internal
29
+ * @category Package : @rest-vir/define-service
30
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
31
+ */
32
+ 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>>}`;
33
+ /**
34
+ * All type safe parameters for sending a request to an endpoint using {@link fetchEndpoint}.
35
+ *
36
+ * @category Internal
37
+ * @category Package : @rest-vir/define-service
38
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
39
+ */
40
+ export type FetchEndpointParams<EndpointToFetch extends SelectFrom<EndpointDefinition, {
41
+ path: true;
42
+ requestDataShape: true;
43
+ responseDataShape: true;
44
+ methods: true;
45
+ }>, AllowFetchMock extends boolean = true> = EndpointToFetch extends EndpointDefinition ? Readonly<(IsNever<PathParams<EndpointToFetch['path']>> extends true ? {
46
+ /** This endpoint has no path parameters to configure. */
47
+ pathParams?: undefined;
48
+ } : PathParams<EndpointToFetch['path']> extends string ? {
49
+ pathParams: Readonly<Record<PathParams<EndpointToFetch['path']>, string>>;
50
+ } : {
51
+ /** This endpoint has no path parameters to configure. */
52
+ pathParams?: undefined;
53
+ }) & (EndpointExecutorData<EndpointToFetch>['request'] extends undefined ? {
54
+ /**
55
+ * This endpoint does not accept any request data, so there is none to be
56
+ * set.
57
+ */
58
+ requestData?: never;
59
+ } : {
60
+ requestData: EndpointExecutorData<EndpointToFetch>['request'];
61
+ }) & (IsNever<FetchMethod<EndpointToFetch>> extends true ? {
62
+ /**
63
+ * This endpoint only allows one method so it does not need to be
64
+ * configured.
65
+ */
66
+ method?: never;
67
+ } : {
68
+ method: FetchMethod<EndpointToFetch>;
69
+ }) & (AllowFetchMock extends true ? Pick<GenericFetchEndpointParams, 'options' | 'fetch'> : Pick<GenericFetchEndpointParams, 'options'>)> : GenericFetchEndpointParams;
70
+ /**
71
+ * Type safe output from sending a request to an endpoint definition. Used by {@link fetchEndpoint}.
72
+ *
73
+ * @category Internal
74
+ * @category Package : @rest-vir/define-service
75
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
76
+ */
77
+ export type FetchEndpointOutput<EndpointToFetch extends Readonly<SelectFrom<EndpointDefinition, {
78
+ requestDataShape: true;
79
+ responseDataShape: true;
80
+ }>> | NoParam> = Readonly<{
81
+ data: EndpointToFetch extends SelectFrom<EndpointDefinition, {
82
+ requestDataShape: true;
83
+ responseDataShape: true;
84
+ }> ? EndpointExecutorData<EndpointToFetch>['response'] : any;
85
+ response: Readonly<Response>;
86
+ }>;
87
+ /**
88
+ * Extracts an array of all allowed methods for the given endpoint definition.
89
+ *
90
+ * @category Internal
91
+ * @category Package : @rest-vir/define-service
92
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
93
+ */
94
+ export declare function getAllowedEndpointMethods(endpoint: Readonly<Pick<EndpointDefinition, 'methods'>>): HttpMethod[];
95
+ /**
96
+ * A wrapper for {@link FetchEndpointParams} that requires parameters based on the endpoint being
97
+ * fetched.
98
+ *
99
+ * @category Internal
100
+ * @category Package : @rest-vir/define-service
101
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
102
+ */
103
+ export type CollapsedFetchEndpointParams<EndpointToFetch extends Readonly<SelectFrom<EndpointDefinition, {
104
+ path: true;
105
+ requestDataShape: true;
106
+ responseDataShape: true;
107
+ methods: true;
108
+ }>> | NoParam, AllowFetchMock extends boolean = true> = EndpointToFetch extends NoParam ? [Readonly<GenericFetchEndpointParams>?] : Readonly<FetchEndpointParams<Exclude<EndpointToFetch, NoParam>, AllowFetchMock>> extends infer RealParams ? RequiredKeysOf<RealParams> extends never ? [RealParams?] : [RealParams] : [];
109
+ /**
110
+ * Send a request to an endpoint definition with type safe parameters.
111
+ *
112
+ * This can safely be used in frontend _or_ backend code.
113
+ *
114
+ * @category Client (Frontend) Connection
115
+ * @category Package : @rest-vir/define-service
116
+ * @example
117
+ *
118
+ * ```ts
119
+ * import {fetchEndpoint} from '@rest-vir/define-service';
120
+ *
121
+ * const {data, response} = await fetchEndpoint(myService.endpoints['/my-endpoint']);
122
+ * ```
123
+ *
124
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
125
+ */
126
+ export declare function fetchEndpoint<const EndpointToFetch extends Readonly<SelectFrom<EndpointDefinition, {
127
+ requestDataShape: true;
128
+ path: true;
129
+ responseDataShape: true;
130
+ methods: true;
131
+ service: {
132
+ serviceOrigin: true;
133
+ serviceName: true;
134
+ };
135
+ }>> | NoParam>(endpoint: EndpointToFetch extends EndpointDefinition ? EndpointToFetch : SelectFrom<EndpointDefinition, {
136
+ requestDataShape: true;
137
+ path: true;
138
+ responseDataShape: true;
139
+ methods: true;
140
+ service: {
141
+ serviceOrigin: true;
142
+ serviceName: true;
143
+ };
144
+ }>, ...params: CollapsedFetchEndpointParams<EndpointToFetch>): Promise<FetchEndpointOutput<EndpointToFetch>>;
145
+ /**
146
+ * Build request init and URL for fetching an endpoint. Used in {@link fetchEndpoint}.
147
+ *
148
+ * @category Internal
149
+ * @category Package : @rest-vir/define-service
150
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
151
+ */
152
+ export declare function buildEndpointRequestInit<const EndpointToFetch extends Readonly<SelectFrom<EndpointDefinition, {
153
+ requestDataShape: true;
154
+ path: true;
155
+ responseDataShape: true;
156
+ methods: true;
157
+ service: {
158
+ serviceOrigin: true;
159
+ serviceName: true;
160
+ };
161
+ }>> | NoParam>(endpoint: EndpointToFetch extends EndpointDefinition ? EndpointToFetch : SelectFrom<EndpointDefinition, {
162
+ requestDataShape: true;
163
+ path: true;
164
+ responseDataShape: true;
165
+ methods: true;
166
+ service: {
167
+ serviceOrigin: true;
168
+ serviceName: true;
169
+ };
170
+ }>, ...[{ method, options, pathParams, requestData },]: CollapsedFetchEndpointParams<EndpointToFetch, false>): {
171
+ url: string;
172
+ requestInit: RequestInit;
173
+ };
174
+ /**
175
+ * Creates and finalizes a URL for sending fetches to the given endpoint.
176
+ *
177
+ * @category Internal
178
+ * @category Package : @rest-vir/define-service
179
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
180
+ */
181
+ export declare function buildEndpointUrl<const EndpointToFetch extends Readonly<SelectFrom<EndpointDefinition, {
182
+ path: true;
183
+ service: {
184
+ serviceOrigin: true;
185
+ serviceName: true;
186
+ };
187
+ methods: true;
188
+ requestDataShape: true;
189
+ responseDataShape: true;
190
+ }>> | NoParam = NoParam>(endpoint: EndpointToFetch extends EndpointDefinition ? EndpointToFetch : SelectFrom<EndpointDefinition, {
191
+ path: true;
192
+ service: {
193
+ serviceOrigin: true;
194
+ serviceName: true;
195
+ };
196
+ requestDataShape: true;
197
+ responseDataShape: true;
198
+ methods: true;
199
+ }>, { pathParams, }: Pick<EndpointToFetch extends NoParam ? Readonly<GenericFetchEndpointParams> : Readonly<FetchEndpointParams<Exclude<EndpointToFetch, NoParam>>>, 'pathParams'>): string;